/// <summary> /// Generates streaming locator using Azure Front Door URL /// This implementation is based on https://github.com/Azure-Samples/media-services-v3-dotnet-core-tutorials/tree/master/NETCore/EncodeHTTPAndPublishAESEncrypted /// </summary> /// <param name="client">Azure Media Services instance client</param> /// <param name="config">Azure Media Services instance configuration</param> /// <param name="locatorName">locator name</param> /// <param name="keyIdentifier">key identifier</param> /// <returns></returns> private async Task <string> GenerateStreamingUrl(IAzureMediaServicesClient client, MediaServiceConfigurationModel config, string locatorName, string keyIdentifier) { // Get token to access content var token = MediaServicesHelper.GetToken(this.configService.TokenIssuer, this.configService.TokenAudience, keyIdentifier, this.configService.GetClearKeyStreamingKey()); // Get list of all paths associated with specific locator var paths = await client.StreamingLocators.ListPathsAsync(config.ResourceGroup, config.AccountName, locatorName).ConfigureAwait(false); // Create Dash URL for (var i = 0; i < paths.StreamingPaths.Count; i++) { var uriBuilder = new UriBuilder(); uriBuilder.Scheme = "https"; uriBuilder.Host = this.configService.FrontDoorHostName; if (paths.StreamingPaths[i].Paths.Count > 0) { if (paths.StreamingPaths[i].StreamingProtocol == StreamingPolicyStreamingProtocol.Dash) { uriBuilder.Path = paths.StreamingPaths[i].Paths[0]; var dashPath = uriBuilder.ToString(); return($"https://ampdemo.azureedge.net/?url={dashPath}&aes=true&aestoken=Bearer%3D{token}"); } } } return(null); }
/// <summary> /// Stores job output status record to storage service /// </summary> /// <param name="mediaServiceAccountName">Account name to load</param> /// <param name="jobOutputStatusModel">Job output status to update</param> /// <param name="job">Job data loaded from API</param> /// <param name="logger">Logger to log data</param> /// <returns>Task for async operation</returns> private async Task UpdateJobOutputStatusAsync(string mediaServiceAccountName, JobOutputStatusModel jobOutputStatusModel, Job job, ILogger logger) { if (job != null) { var statusInfo = MediaServicesHelper.GetJobOutputState(job, jobOutputStatusModel.JobOutputAssetName); var jobOutputStatusModelFromAPI = new JobOutputStatusModel { Id = Guid.NewGuid().ToString(), EventTime = statusInfo.Item2, JobOutputState = statusInfo.Item1, JobName = jobOutputStatusModel.JobName, MediaServiceAccountName = mediaServiceAccountName, JobOutputAssetName = jobOutputStatusModel.JobOutputAssetName, TransformName = jobOutputStatusModel.TransformName }; // Provisioning request is created for all job output status events that are finished. if (jobOutputStatusModelFromAPI.JobOutputState == JobState.Finished) { var provisioningRequestResult = await this.provisioningRequestStorageService.CreateAsync( new ProvisioningRequestModel { Id = Guid.NewGuid().ToString(), ProcessedAssetMediaServiceAccountName = jobOutputStatusModelFromAPI.MediaServiceAccountName, ProcessedAssetName = jobOutputStatusModelFromAPI.JobOutputAssetName, StreamingLocatorName = $"streaming-{jobOutputStatusModelFromAPI.JobOutputAssetName}" }, logger).ConfigureAwait(false); logger.LogInformation($"JobOutputStatusSyncService::UpdateJobOutputStatusAsync created stream provisioning request: result={LogHelper.FormatObjectForLog(provisioningRequestResult)}"); } await this.jobOutputStatusStorageService.CreateOrUpdateAsync(jobOutputStatusModelFromAPI, logger).ConfigureAwait(false); } }
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, TraceWriter log) { log.Info($"AMS v3 Function - CreateEmptyAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.contentKeyPolicyName == null) { return(new BadRequestObjectResult("Please pass contentKeyPolicyName in the input object")); } if (data.contentKeyPolicyOptions == null) { return(new BadRequestObjectResult("Please pass contentKeyPolicyOptions in the input object")); } string contentKeyPolicyName = data.contentKeyPolicyName; string contentKeyPolicyDescription = null; if (data.contentKeyPolicyDescription == null) { contentKeyPolicyDescription = data.contentKeyPolicyDescription; } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); ContentKeyPolicy policy = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); JsonConverter[] jsonConverters = { new MediaServicesHelperJsonConverter(), new MediaServicesHelperTimeSpanJsonConverter() }; List <ContentKeyPolicyOption> options = JsonConvert.DeserializeObject <List <ContentKeyPolicyOption> >(data.contentKeyPolicyOptions.ToString(), jsonConverters); policy = client.ContentKeyPolicies.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, contentKeyPolicyName, options, contentKeyPolicyDescription); } catch (ApiErrorException e) { log.Info($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.Info($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { policyId = policy.PolicyId })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - CreateEmptyAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.assetNamePrefix == null) { return(new BadRequestObjectResult("Please pass assetNamePrefix in the input object")); } string assetStorageAccount = null; if (data.assetStorageAccount != null) { assetStorageAccount = data.assetStorageAccount; } Guid assetGuid = Guid.NewGuid(); string assetName = data.assetNamePrefix + "-" + assetGuid.ToString(); MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); Asset asset = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); Asset assetParams = new Asset(null, assetName, null, assetGuid, DateTime.Now, DateTime.Now, null, assetName, null, assetStorageAccount, AssetStorageEncryptionFormat.None); asset = client.Assets.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, assetName, assetParams); //asset = client.Assets.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, assetName, new Asset()); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } // compatible with AMS V2 API string assetId = "nb:cid:UUID:" + asset.AssetId; string destinationContainer = "asset-" + asset.AssetId; return((ActionResult) new OkObjectResult(new { assetName = assetName, assetId = assetId, destinationContainer = destinationContainer })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - PublishAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.streamingLocatorName == null) { return(new BadRequestObjectResult("Please pass streamingLocatorName in the input object")); } string streamingLocatorName = data.streamingLocatorName; string streamingPolicyName = null; MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); string contentKeysJson = string.Empty; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); var streamingLocator = client.StreamingLocators.Get(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName); streamingPolicyName = streamingLocator.StreamingPolicyName; client.StreamingLocators.Delete(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName); if (!streamingPolicyName.StartsWith("Predefined_")) { client.StreamingPolicies.Delete(amsconfig.ResourceGroup, amsconfig.AccountName, streamingPolicyName); } } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { streamingLocatorName = streamingLocatorName, streamingPolicyName = streamingPolicyName == null || streamingPolicyName.StartsWith("Predefined_") ? "None" : streamingPolicyName, contentKeys = contentKeysJson })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - GetTransform was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.transformName == null) { return(new BadRequestObjectResult("Please pass transformName in the input object")); } string transformName = data.transformName; MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); string transformId = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); Transform transform = client.Transforms.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName); if (transform == null) { transformName = null; } else { transformId = transform.Id; } } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { transformName = transformName, transformId = transformId })); }
public async Task SubmitTestRequests() { var jobOutputStatusStorageService = new JobOutputStatusStorageService(jobOutputStatusTableStorageService); var mediaServiceInstanceHealthStorageService = new MediaServiceInstanceHealthStorageService(mediaServiceInstanceHealthTableStorageService); var mediaServiceInstanceHealthService = new MediaServiceInstanceHealthService(mediaServiceInstanceHealthStorageService, jobOutputStatusStorageService, mediaServiceCallHistoryStorageService, configService); var mediaServiceInstanceFactory = new MediaServiceInstanceFactory(mediaServiceCallHistoryStorageService, configService); foreach (var config in configService.MediaServiceInstanceConfiguration) { var client = mediaServiceInstanceFactory.GetMediaServiceInstance(config.Value.AccountName, Mock.Of <ILogger>()); client.LongRunningOperationRetryTimeout = 2; await MediaServicesHelper.EnsureTransformExists( client, config.Value.ResourceGroup, config.Value.AccountName, transformName, new BuiltInStandardEncoderPreset(EncoderNamedPreset.AdaptiveStreaming)).ConfigureAwait(false); await mediaServiceInstanceHealthService.CreateOrUpdateAsync(new MediaServiceInstanceHealthModel { MediaServiceAccountName = config.Value.AccountName, HealthState = InstanceHealthState.Healthy, LastUpdated = DateTime.UtcNow, IsEnabled = true }, Mock.Of <ILogger>()).ConfigureAwait(false); await MediaServicesHelper.EnsureContentKeyPolicyExists( client, config.Value.ResourceGroup, config.Value.AccountName, configService.ContentKeyPolicyName, configService.GetClearKeyStreamingKey(), configService.TokenIssuer, configService.TokenAudience).ConfigureAwait(false); } var target = new JobRequestStorageService(jobRequestQueue); var uniqueness = Guid.NewGuid().ToString().Substring(0, 13); for (var i = 0; i < 10; i++) { Assert.IsNotNull(await target.CreateAsync(GenerateJobRequestModel(i, uniqueness), Mock.Of <ILogger>()).ConfigureAwait(false)); } }
public static async Task Run([EventGridTrigger] EventGridEvent eventGridEvent, ILogger log) { log.LogInformation(eventGridEvent.Data.ToString()); dynamic data = eventGridEvent.Data; if (data["state"] != "Processing") { log.LogInformation($"jobStateChange.Data.State = {data["state"]}, nothting to do"); return; } var azureServiceTokenProvider = new AzureServiceTokenProvider(); string accessToken = await azureServiceTokenProvider.GetAccessTokenAsync("https://rest.media.azure.net"); //var req = await MediaServicesHelper.ScaleUpReservedUnits(httpClient, accessToken); //log.LogInformation($"Set Reserved Units to {req.value[0].CurrentReservedUnits}"); var req = await MediaServicesHelper.ScaleDownReservedUnits(httpClient, accessToken); log.LogInformation($"Set Reserved Units to {req.value[0].CurrentReservedUnits}"); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - DeleteAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); string assetName = data.assetName; string accountName = data.accountName; string resourceGroup = data.resourceGroup; MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); client.Assets.Delete(resourceGroup, accountName, assetName); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } // compatible with AMS V2 API return((ActionResult) new OkObjectResult(new { assetName = assetName })); }
public static async Task <object> Run([HttpTrigger(WebHookType = "genericJson")] HttpRequestMessage req, TraceWriter log) { log.Info($"AMS v2 Function - CreateContentKeyAuthorizationPolicy was triggered!"); string jsonContent = await req.Content.ReadAsStringAsync(); dynamic data = JsonConvert.DeserializeObject(jsonContent); // Validate input objects if (data.assetId == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass assetId in the input object" })); } if (data.contentKeyAuthorizationPolicyId == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass contentKeyAuthorizationPolicyId in the input object" })); } if (data.assetDeliveryPolicyId == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass assetDeliveryPolicyId in the input object" })); } if (data.contentKeyType == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass contentKeyType in the input object" })); } string assetId = data.assetId; string contentKeyAuthorizationPolicyId = data.contentKeyAuthorizationPolicyId; string assetDeliveryPolicyId = data.assetDeliveryPolicyId; string contentKeyTypeName = data.contentKeyType; if (!MediaServicesHelper.AMSContentKeyType.ContainsKey(contentKeyTypeName)) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass a valid contentKeyType in the input object" })); } ContentKeyType contentKeyType = MediaServicesHelper.AMSContentKeyType[contentKeyTypeName]; if (contentKeyType != ContentKeyType.CommonEncryption && contentKeyType != ContentKeyType.CommonEncryptionCbcs && contentKeyType != ContentKeyType.EnvelopeEncryption) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass a valid contentKeyType in the input object" })); } string contentKeyName = null; if (data.contentKeyName != null) { contentKeyName = data.contentKeyName; } MediaServicesCredentials amsCredentials = new MediaServicesCredentials(); IAsset asset = null; IContentKeyAuthorizationPolicy ckaPolicy = null; IAssetDeliveryPolicy adPolicy = null; IContentKey contentKey = null; try { // Load AMS account context log.Info($"Using AMS v2 REST API Endpoint : {amsCredentials.AmsRestApiEndpoint.ToString()}"); AzureAdTokenCredentials tokenCredentials = new AzureAdTokenCredentials(amsCredentials.AmsAadTenantDomain, new AzureAdClientSymmetricKey(amsCredentials.AmsClientId, amsCredentials.AmsClientSecret), AzureEnvironments.AzureCloudEnvironment); AzureAdTokenProvider tokenProvider = new AzureAdTokenProvider(tokenCredentials); _context = new CloudMediaContext(amsCredentials.AmsRestApiEndpoint, tokenProvider); // Get the Asset, ContentKeyAuthorizationPolicy, AssetDeliveryPolicy asset = _context.Assets.Where(a => a.Id == assetId).FirstOrDefault(); if (asset == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Asset not found" })); } ckaPolicy = _context.ContentKeyAuthorizationPolicies.Where(p => p.Id == contentKeyAuthorizationPolicyId).Single(); if (ckaPolicy == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "ContentKeyAuthorizationPolicy not found" })); } adPolicy = _context.AssetDeliveryPolicies.Where(p => p.Id == assetDeliveryPolicyId).Single(); if (adPolicy == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "AssetDeliveryPolicy not found" })); } switch (contentKeyType) { case ContentKeyType.CommonEncryption: if (contentKeyName == null) { contentKeyName = "Common Encryption ContentKey"; } contentKey = MediaServicesHelper.CreateContentKey(_context, contentKeyName, ContentKeyType.CommonEncryption); break; case ContentKeyType.CommonEncryptionCbcs: if (contentKeyName == null) { contentKeyName = "Common Encryption CBCS ContentKey"; } contentKey = MediaServicesHelper.CreateContentKey(_context, contentKeyName, ContentKeyType.CommonEncryptionCbcs); break; case ContentKeyType.EnvelopeEncryption: if (contentKeyName == null) { contentKeyName = "Envelope Encryption ContentKey"; } contentKey = MediaServicesHelper.CreateContentKey(_context, contentKeyName, ContentKeyType.EnvelopeEncryption); break; } asset.ContentKeys.Add(contentKey); contentKey.AuthorizationPolicyId = ckaPolicy.Id; contentKey = contentKey.UpdateAsync().Result; asset.DeliveryPolicies.Add(adPolicy); } catch (Exception e) { log.Info($"ERROR: Exception {e}"); return(req.CreateResponse(HttpStatusCode.BadRequest)); } return(req.CreateResponse(HttpStatusCode.OK, new { contentKeyId = contentKey.Id })); }
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, TraceWriter log) { log.Info($"AMS v3 Function - CreateTransform was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.transformName == null) { return(new BadRequestObjectResult("Please pass transformName in the input object")); } string transformName = data.transformName; if (data.transformOutputs == null) { return(new BadRequestObjectResult("Please pass transformOutputs in the input object")); } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); string transformId = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); // Does a Transform already exist with the desired name? Assume that an existing Transform with the desired name // also uses the same recipe or Preset for processing content. Transform transform = client.Transforms.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName); if (transform == null) { // You need to specify what you want it to produce as an output JsonConverter[] jsonConverters = { new MediaServicesHelperJsonConverter(), new MediaServicesHelperTimeSpanJsonConverter() }; List <TransformOutput> transformOutputList = JsonConvert.DeserializeObject <List <TransformOutput> >(data.transformOutputs.ToString(), jsonConverters); // You need to specify what you want it to produce as an output TransformOutput[] output = transformOutputList.ToArray(); // Create the Transform with the output defined above transform = client.Transforms.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, transformName, output); } transformId = transform.Id; } catch (ApiErrorException e) { log.Info($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.Info($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { transformId = transformId })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - CreateStreamingPolicy was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.streamingPolicyName == null) { return(new BadRequestObjectResult("Please pass streamingPolicyName in the input object")); } if (data.defaultContentKeyPolicyName == null) { return(new BadRequestObjectResult("Please pass defaultContentKeyPolicyName in the input object")); } string streamingPolicyName = data.streamingPolicyName; string defaultContentKeyPolicyName = data.defaultContentKeyPolicyName; string mode = data.mode; if (mode != "simple" && mode != "advanced") { return(new BadRequestObjectResult("Please pass valid mode in the input object")); } // // Simple Mode // if (mode == "simple") { if (data.noEncryptionProtocols == null && data.cbcsProtocols == null && data.cencProtocols == null && data.envelopeProtocols == null) { return(new BadRequestObjectResult("Please pass one protocol for any encryption scheme at least in the input object")); } } // // Advanced Mode // if (mode == "advanced") { if (data.jsonNoEncryption == null && data.jsonCommonEncryptionCbcs == null && data.jsonCommonEncryptionCenc == null && data.jsonEnvelopeEncryption == null) { return(new BadRequestObjectResult("Please pass one encryption scheme JSON at least in the input object")); } } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); StreamingPolicy policy = null; JsonConverter[] jsonReaders = { new MediaServicesHelperJsonReader(), new MediaServicesHelperTimeSpanJsonConverter() }; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); policy = client.StreamingPolicies.Get(amsconfig.ResourceGroup, amsconfig.AccountName, streamingPolicyName); if (policy == null) { StreamingPolicy parameters = new StreamingPolicy(); parameters.DefaultContentKeyPolicyName = defaultContentKeyPolicyName; if (mode == "simple") { // NoEncryption Arguments if (data.noEncryptionProtocols != null) { String[] noEncryptionProtocols = data.noEncryptionProtocols.ToString().Split(';'); if (Array.IndexOf(noEncryptionProtocols, "Dash") > -1) { parameters.NoEncryption.EnabledProtocols.Dash = true; } if (Array.IndexOf(noEncryptionProtocols, "Download") > -1) { parameters.NoEncryption.EnabledProtocols.Download = true; } if (Array.IndexOf(noEncryptionProtocols, "Hls") > -1) { parameters.NoEncryption.EnabledProtocols.Hls = true; } if (Array.IndexOf(noEncryptionProtocols, "SmoothStreaming") > -1) { parameters.NoEncryption.EnabledProtocols.SmoothStreaming = true; } } // Common Encryption CBCS Argument if (data.cbcsClearTracks != null) { List <TrackSelection> tracks = JsonConvert.DeserializeObject <List <TrackSelection> >(data.cbcsClearTracks.ToString(), jsonReaders); parameters.CommonEncryptionCbcs.ClearTracks = tracks; } if (data.cbcsDefaultKeyLabel != null) { parameters.CommonEncryptionCbcs.ContentKeys.DefaultKey.Label = data.cbcsDefaultKeyLabel; } if (data.cbcsDefaultKeyPolicyName != null) { parameters.CommonEncryptionCbcs.ContentKeys.DefaultKey.PolicyName = data.cbcsDefaultKeyPolicyName; } if (data.cbcsClearTracks != null) { List <StreamingPolicyContentKey> mappings = JsonConvert.DeserializeObject <List <StreamingPolicyContentKey> >(data.cbcsKeyToTrackMappings.ToString(), jsonReaders); parameters.CommonEncryptionCbcs.ContentKeys.KeyToTrackMappings = mappings; } if (data.cbcsFairPlayTemplate != null) { parameters.CommonEncryptionCbcs.Drm.FairPlay.CustomLicenseAcquisitionUrlTemplate = data.cbcsFairPlayTemplate; } if (data.cbcsFairPlayAllowPersistentLicense != null) { parameters.CommonEncryptionCbcs.Drm.FairPlay.AllowPersistentLicense = data.cbcsFairPlayAllowPersistentLicense; } if (data.cbcsPlayReadyTemplate != null) { parameters.CommonEncryptionCbcs.Drm.PlayReady.CustomLicenseAcquisitionUrlTemplate = data.cbcsPlayReadyTemplate; } if (data.cbcsPlayReadyAttributes != null) { parameters.CommonEncryptionCbcs.Drm.PlayReady.PlayReadyCustomAttributes = data.cbcsPlayReadyAttributes; } if (data.cbcsWidevineTemplate != null) { parameters.CommonEncryptionCbcs.Drm.Widevine.CustomLicenseAcquisitionUrlTemplate = data.cbcsWidevineTemplate; } if (data.cbcsProtocols != null) { String[] commonEncryptionCbcsProtocols = data.cbcsProtocols.ToString().Split(';'); if (Array.IndexOf(commonEncryptionCbcsProtocols, "Dash") > -1) { parameters.CommonEncryptionCbcs.EnabledProtocols.Dash = true; } if (Array.IndexOf(commonEncryptionCbcsProtocols, "Download") > -1) { parameters.CommonEncryptionCbcs.EnabledProtocols.Download = true; } if (Array.IndexOf(commonEncryptionCbcsProtocols, "Hls") > -1) { parameters.CommonEncryptionCbcs.EnabledProtocols.Hls = true; } if (Array.IndexOf(commonEncryptionCbcsProtocols, "SmoothStreaming") > -1) { parameters.CommonEncryptionCbcs.EnabledProtocols.SmoothStreaming = true; } } // Common Encryption CENC Argument if (data.cencClearTracks != null) { List <TrackSelection> tracks = JsonConvert.DeserializeObject <List <TrackSelection> >(data.cencClearTracks.ToString(), jsonReaders); parameters.CommonEncryptionCenc.ClearTracks = tracks; } if (data.cencDefaultKeyLabel != null) { parameters.CommonEncryptionCenc.ContentKeys.DefaultKey.Label = data.cencDefaultKeyLabel; } if (data.cencDefaultKeyPolicyName != null) { parameters.CommonEncryptionCenc.ContentKeys.DefaultKey.PolicyName = data.cencDefaultKeyPolicyName; } if (data.cencClearTracks != null) { List <StreamingPolicyContentKey> mappings = JsonConvert.DeserializeObject <List <StreamingPolicyContentKey> >(data.cencKeyToTrackMappings.ToString(), jsonReaders); parameters.CommonEncryptionCenc.ContentKeys.KeyToTrackMappings = mappings; } if (data.cencPlayReadyTemplate != null) { parameters.CommonEncryptionCenc.Drm.PlayReady.CustomLicenseAcquisitionUrlTemplate = data.cencPlayReadyTemplate; } if (data.cencPlayReadyAttributes != null) { parameters.CommonEncryptionCenc.Drm.PlayReady.PlayReadyCustomAttributes = data.cencPlayReadyAttributes; } if (data.cencWidevineTemplate != null) { parameters.CommonEncryptionCenc.Drm.Widevine.CustomLicenseAcquisitionUrlTemplate = data.cencWidevineTemplate; } if (data.cencProtocols != null) { String[] commonEncryptionCencProtocols = data.cencProtocols.ToString().Split(';'); if (Array.IndexOf(commonEncryptionCencProtocols, "Dash") > -1) { parameters.CommonEncryptionCenc.EnabledProtocols.Dash = true; } if (Array.IndexOf(commonEncryptionCencProtocols, "Download") > -1) { parameters.CommonEncryptionCenc.EnabledProtocols.Download = true; } if (Array.IndexOf(commonEncryptionCencProtocols, "Hls") > -1) { parameters.CommonEncryptionCenc.EnabledProtocols.Hls = true; } if (Array.IndexOf(commonEncryptionCencProtocols, "SmoothStreaming") > -1) { parameters.CommonEncryptionCenc.EnabledProtocols.SmoothStreaming = true; } } // Envelope Encryption Argument if (data.envelopeClearTracks != null || data.envelopeDefaultKeyLabel != null || data.envelopeDefaultKeyPolicyName != null || data.envelopeClearTracks || data.envelopeTemplate != null || data.envelopeTemplate != null || data.envelopeProtocols != null) { parameters.EnvelopeEncryption = new EnvelopeEncryption(); if (data.envelopeClearTracks != null) { List <TrackSelection> tracks = JsonConvert.DeserializeObject <List <TrackSelection> >(data.envelopeClearTracks.ToString(), jsonReaders); parameters.EnvelopeEncryption.ClearTracks = tracks; } parameters.EnvelopeEncryption.ContentKeys = new StreamingPolicyContentKeys(); parameters.EnvelopeEncryption.ContentKeys.DefaultKey = new DefaultKey(data.envelopeDefaultKeyLabel as string, data.envelopeDefaultKeyPolicyName as string); if (data.envelopeClearTracks != null) { List <StreamingPolicyContentKey> mappings = JsonConvert.DeserializeObject <List <StreamingPolicyContentKey> >(data.envelopeKeyToTrackMappings.ToString(), jsonReaders); parameters.EnvelopeEncryption.ContentKeys.KeyToTrackMappings = mappings; } if (data.envelopeTemplate != null) { parameters.EnvelopeEncryption.CustomKeyAcquisitionUrlTemplate = data.envelopeTemplate; } if (data.envelopeProtocols != null) { parameters.EnvelopeEncryption.EnabledProtocols = new EnabledProtocols(); String[] envelopeEncryptionProtocols = data.envelopeProtocols.ToString().Split(';'); if (Array.IndexOf(envelopeEncryptionProtocols, "Dash") > -1) { parameters.EnvelopeEncryption.EnabledProtocols.Dash = true; } if (Array.IndexOf(envelopeEncryptionProtocols, "Download") > -1) { parameters.EnvelopeEncryption.EnabledProtocols.Download = true; } if (Array.IndexOf(envelopeEncryptionProtocols, "Hls") > -1) { parameters.EnvelopeEncryption.EnabledProtocols.Hls = true; } if (Array.IndexOf(envelopeEncryptionProtocols, "SmoothStreaming") > -1) { parameters.EnvelopeEncryption.EnabledProtocols.SmoothStreaming = true; } } } } else if (mode == "advanced") { NoEncryption noEncryptionArguments = null; CommonEncryptionCbcs commonEncryptionCbcsArguments = null; CommonEncryptionCenc commonEncryptionCencArguments = null; EnvelopeEncryption envelopeEncryptionArguments = null; if (data.jsonNoEncryption != null) { noEncryptionArguments = JsonConvert.DeserializeObject <NoEncryption>(data.configNoEncryption.ToString(), jsonReaders); } if (data.jsonCommonEncryptionCbcs != null) { commonEncryptionCbcsArguments = JsonConvert.DeserializeObject <CommonEncryptionCbcs>(data.jsonCommonEncryptionCbcs.ToString(), jsonReaders); } if (data.jsonCommonEncryptionCenc != null) { commonEncryptionCencArguments = JsonConvert.DeserializeObject <CommonEncryptionCenc>(data.jsonCommonEncryptionCenc.ToString(), jsonReaders); } if (data.jsonEnvelopeEncryption != null) { envelopeEncryptionArguments = JsonConvert.DeserializeObject <EnvelopeEncryption>(data.jsonEnvelopeEncryption.ToString(), jsonReaders); } parameters.NoEncryption = noEncryptionArguments; parameters.CommonEncryptionCbcs = commonEncryptionCbcsArguments; parameters.CommonEncryptionCenc = commonEncryptionCencArguments; parameters.EnvelopeEncryption = envelopeEncryptionArguments; } parameters.Validate(); policy = client.StreamingPolicies.Create(amsconfig.ResourceGroup, amsconfig.AccountName, streamingPolicyName, parameters); } } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { streamingPolicyName = streamingPolicyName, streamingPolicyId = policy.Id })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - MonitorMediaJob was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.jobName == null) { return(new BadRequestObjectResult("Please pass jobName in the input object")); } if (data.transformName == null) { return(new BadRequestObjectResult("Please pass transformName in the input object")); } string jobName = data.jobName; string transformName = data.transformName; MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); Job job = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); job = client.Jobs.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName, jobName); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } JObject result = new JObject(); result["jobStatus"] = job.State.ToString(); JArray jobOutputStateList = new JArray(); foreach (JobOutputAsset o in job.Outputs) { JObject jobOutputState = new JObject(); jobOutputState["AssetName"] = o.AssetName; jobOutputState["State"] = o.State.ToString(); jobOutputState["Progress"] = o.Progress; if (o.Error != null) { jobOutputState["ErrorCode"] = o.Error.Code.ToString(); jobOutputState["ErrorMessage"] = o.Error.Message.ToString(); } jobOutputStateList.Add(jobOutputState); } result["jobOutputStateList"] = jobOutputStateList; return((ActionResult) new OkObjectResult(result)); }
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, TraceWriter log) { log.Info($"AMS v3 Function - PublishAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.assetName == null) { return(new BadRequestObjectResult("Please pass assetName in the input object")); } if (data.streamingPolicyName == null) { return(new BadRequestObjectResult("Please pass streamingPolicyName in the input object")); } string assetName = data.assetName; string streamingPolicyName = data.streamingPolicyName; DateTime startDateTime = new DateTime(0); if (data.startDateTime != null) { startDateTime = data.startDateTime; } DateTime endDateTime = new DateTime(0); if (data.endDateTime != null) { endDateTime = data.endDateTime; } Guid streamingLocatorId = Guid.NewGuid(); if (data.streamingLocatorId != null) { streamingLocatorId = new Guid((string)(data.streamingLocatorId)); } string defaultContentKeyPolicyName = null; if (data.defaultContentKeyPolicyName != null) { defaultContentKeyPolicyName = data.defaultContentKeyPolicyName; } List <StreamingLocatorUserDefinedContentKey> contentKeys = new List <StreamingLocatorUserDefinedContentKey>(); MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); string streamingLocatorName = "streaminglocator-" + streamingLocatorId.ToString(); StreamingLocator streamingLocator = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); streamingLocator = new StreamingLocator() { AssetName = assetName, StreamingPolicyName = streamingPolicyName, StartTime = null, EndTime = null, StreamingLocatorId = streamingLocatorId, DefaultContentKeyPolicyName = defaultContentKeyPolicyName, }; if (!startDateTime.Equals(new DateTime(0))) { streamingLocator.StartTime = startDateTime; } if (!endDateTime.Equals(new DateTime(0))) { streamingLocator.EndTime = endDateTime; } streamingLocator.Validate(); client.StreamingLocators.Create(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName, streamingLocator); } catch (ApiErrorException e) { log.Info($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.Info($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { streamingLocatorName = streamingLocatorName, streamingLocatorId = streamingLocator.StreamingLocatorId.ToString() })); }
/// <summary> /// Verifies the status of given job, implements business logic to resubmit jobs if needed /// </summary> /// <param name="jobVerificationRequestModel">Job verification request</param> /// <param name="logger">Logger to log data</param> /// <returns>Processed job verification request</returns> public async Task <JobVerificationRequestModel> VerifyJobAsync(JobVerificationRequestModel jobVerificationRequestModel, ILogger logger) { logger.LogInformation($"JobVerificationService::VerifyJobAsync started: jobVerificationRequestModel={LogHelper.FormatObjectForLog(jobVerificationRequestModel)}"); // Get latest job output status from storage service. var jobOutputStatus = await this.jobOutputStatusStorageService.GetLatestJobOutputStatusAsync(jobVerificationRequestModel.JobName, jobVerificationRequestModel.JobOutputAssetName).ConfigureAwait(false); var jobOutputStatusLoadedFromAPI = false; // if job has not reached final state, need to reload status from Azure Media Service APIs in case of delayed or lost EventGrid event. if (jobOutputStatus?.JobOutputState != JobState.Finished && jobOutputStatus?.JobOutputState != JobState.Error && jobOutputStatus?.JobOutputState != JobState.Canceled) { var clientConfiguration = this.configService.MediaServiceInstanceConfiguration[jobVerificationRequestModel.MediaServiceAccountName]; var clientInstance = this.mediaServiceInstanceFactory.GetMediaServiceInstance(jobVerificationRequestModel.MediaServiceAccountName, logger); logger.LogInformation($"JobVerificationService::VerifyJobAsync checking job status using API: mediaServiceInstanceName={jobVerificationRequestModel.MediaServiceAccountName}"); // Get job data to verify status of specific job output. var job = await clientInstance.Jobs.GetAsync(clientConfiguration.ResourceGroup, clientConfiguration.AccountName, jobVerificationRequestModel.OriginalJobRequestModel.TransformName, jobVerificationRequestModel.JobName).ConfigureAwait(false); logger.LogInformation($"JobVerificationService::VerifyJobAsync loaded job data from API: job={LogHelper.FormatObjectForLog(job)}"); if (job != null) { // create job output status record using job loaded from Azure Media Service API. var statusInfo = MediaServicesHelper.GetJobOutputState(job, jobVerificationRequestModel.JobOutputAssetName); jobOutputStatus = new JobOutputStatusModel { Id = Guid.NewGuid().ToString(), EventTime = statusInfo.Item2, JobOutputState = statusInfo.Item1, JobName = job.Name, MediaServiceAccountName = jobVerificationRequestModel.MediaServiceAccountName, JobOutputAssetName = jobVerificationRequestModel.JobOutputAssetName, TransformName = jobVerificationRequestModel.OriginalJobRequestModel.TransformName, HasRetriableError = MediaServicesHelper.HasRetriableError(job, jobVerificationRequestModel.JobOutputAssetName) // check if job should be retried }; jobOutputStatusLoadedFromAPI = true; // persist job output status record await this.jobOutputStatusStorageService.CreateOrUpdateAsync(jobOutputStatus, logger).ConfigureAwait(false); } } // At this point here, jobOutputStatus is either loaded from job output status storage or from Azure Media Service API. logger.LogInformation($"JobVerificationService::VerifyJobAsync jobOutputStatus={LogHelper.FormatObjectForLog(jobOutputStatus)}"); // Check if job output has been successfully finished. if (jobOutputStatus?.JobOutputState == JobState.Finished) { await this.ProcessFinishedJobAsync(jobVerificationRequestModel, jobOutputStatusLoadedFromAPI, logger).ConfigureAwait(false); logger.LogInformation($"JobVerificationService::VerifyJobAsync] job was completed successfully: jobOutputStatus={LogHelper.FormatObjectForLog(jobOutputStatus)}"); return(jobVerificationRequestModel); } // Check if job output failed. if (jobOutputStatus?.JobOutputState == JobState.Error) { await this.ProcessFailedJob(jobVerificationRequestModel, jobOutputStatus, logger).ConfigureAwait(false); logger.LogInformation($"JobVerificationService::VerifyJobAsync] job failed: jobOutputStatus={LogHelper.FormatObjectForLog(jobOutputStatus)}"); return(jobVerificationRequestModel); } // check if job has been canceled. if (jobOutputStatus?.JobOutputState == JobState.Canceled) { logger.LogInformation($"JobVerificationService::VerifyJobAsync] job canceled: jobOutputStatus={LogHelper.FormatObjectForLog(jobOutputStatus)}"); return(jobVerificationRequestModel); } // At this point, job is stuck, it is not in the final state and long enough time period has passed (since this code is running for a given job). await this.ProcessStuckJob(jobVerificationRequestModel, logger).ConfigureAwait(false); logger.LogInformation($"JobVerificationService::VerifyJobAsync completed: job={LogHelper.FormatObjectForLog(jobVerificationRequestModel)}"); return(jobVerificationRequestModel); }
/// <summary> /// Parses data from EventGridEvent and creates JobOutputStatusModel. /// </summary> /// <param name="eventGridEvent">Data to parse</param> /// <param name="logger">Logger to log data</param> /// <returns>Parsed job output status model</returns> public JobOutputStatusModel ParseEventData(EventGridEvent eventGridEvent, ILogger logger) { var eventId = eventGridEvent.Id; var amsAccountResourceId = eventGridEvent.Topic; var jobId = eventGridEvent.Subject; var eventTime = eventGridEvent.EventTime; // jobName is parsed out of event subject. // Event subject example "transforms/TestTransform/jobs/jobName-5-691d0385-cfe3 var jobName = string.Empty; var match = Regex.Match(jobId, @".+/(?<jobname>.+)"); if (match.Success) { jobName = match.Groups["jobname"].ToString(); } if (string.IsNullOrEmpty(jobName)) { logger.LogError($"EventGridService::ParseEventData failed to parse job name, eventGridEvent={LogHelper.FormatObjectForLog(eventGridEvent)}"); return(null); } // account name is parsed from topic // topic example /subscriptions/<subscriptionId>/resourceGroups/<groupName>/providers/Microsoft.Media/mediaservices/<accountName> var amsAccountName = ""; var matchAccount = Regex.Match(amsAccountResourceId, @".+/(?<accountname>.+)"); if (matchAccount.Success) { amsAccountName = matchAccount.Groups["accountname"].ToString(); } if (string.IsNullOrEmpty(amsAccountName)) { logger.LogError($"EventGridService::ParseEventData failed to parse MSA account name, eventGridEvent={LogHelper.FormatObjectForLog(eventGridEvent)}"); return(null); } var mediaJobOutputStateChangeEventData = (MediaJobOutputStateChangeEventData)eventGridEvent.Data; var asset = (MediaJobOutputAsset)mediaJobOutputStateChangeEventData.Output; if (asset == null) { logger.LogInformation($"EventGridService::ParseEventData asset is null"); return(null); } var jobOutputStatusModel = new JobOutputStatusModel { Id = eventId, JobName = jobName, JobOutputAssetName = asset.AssetName, JobOutputState = asset.State.ToString(), EventTime = eventTime, MediaServiceAccountName = amsAccountName, HasRetriableError = MediaServicesHelper.HasRetriableError(asset) }; logger.LogInformation($"EventGridService::ParseEventData successfully parsed, jobOutputStatusModel={LogHelper.FormatObjectForLog(jobOutputStatusModel)}"); return(jobOutputStatusModel); }
public static async Task <object> Run([HttpTrigger(WebHookType = "genericJson")] HttpRequestMessage req, TraceWriter log) { log.Info($"AMS v2 Function - SubmitMediaJob was triggered!"); string jsonContent = await req.Content.ReadAsStringAsync(); dynamic data = JsonConvert.DeserializeObject(jsonContent); // Validate input objects if (data.assetId == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass assetId in the input object" })); } if (data.mediaTasks == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass mediaTasks in the input object" })); } string assetId = data.assetId; List <AMSMediaTask> mediaTasks = ((JArray)data.mediaTasks).ToObject <List <AMSMediaTask> >(); int jobPriority = 10; if (data.jobPriority != null) { jobPriority = data.jobPriority; } string jobName = "Azure Functions - Media Processing Job"; if (data.jobName != null) { jobName = data.jobName; } MediaServicesCredentials amsCredentials = new MediaServicesCredentials(); IAsset asset = null; IJob job = null; uint taskId = 0; try { // Load AMS account context log.Info($"Using AMS v2 REST API Endpoint : {amsCredentials.AmsRestApiEndpoint.ToString()}"); AzureAdTokenCredentials tokenCredentials = new AzureAdTokenCredentials(amsCredentials.AmsAadTenantDomain, new AzureAdClientSymmetricKey(amsCredentials.AmsClientId, amsCredentials.AmsClientSecret), AzureEnvironments.AzureCloudEnvironment); AzureAdTokenProvider tokenProvider = new AzureAdTokenProvider(tokenCredentials); _context = new CloudMediaContext(amsCredentials.AmsRestApiEndpoint, tokenProvider); // Get the Asset asset = _context.Assets.Where(a => a.Id == assetId).FirstOrDefault(); if (asset == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Asset not found" })); } // Declare a new Media Processing Job job = _context.Jobs.Create(jobName + " - " + asset.Name + " [" + assetId + "]"); job.Priority = jobPriority; foreach (AMSMediaTask mediaTask in mediaTasks) { ITask task = null; IMediaProcessor processor = MediaServicesHelper.GetLatestMediaProcessorByName(_context, mediaTask.mediaProcessor); if (mediaTask.configuration.StartsWith(base64encodedstringprefix)) { byte[] b64decoded = Convert.FromBase64String(mediaTask.configuration.Substring(base64encodedstringprefix.Length)); mediaTask.configuration = System.Text.ASCIIEncoding.ASCII.GetString(b64decoded); } task = job.Tasks.AddNew(mediaTask.mediaTaskName, processor, mediaTask.configuration, TaskOptions.None); if (mediaTask.additionalInputAssetIds != null) { foreach (string inputAssetId in mediaTask.additionalInputAssetIds) { IAsset aAsset = _context.Assets.Where(a => a.Id == inputAssetId).FirstOrDefault(); task.InputAssets.Add(aAsset); } } // Add primary input asset at last task.InputAssets.Add(asset); string outputAssetName = asset.Name + " - " + mediaTask.mediaProcessor; IAsset outputAsset = task.OutputAssets.AddNew(outputAssetName, mediaTask.outputStorageAccount, asset.Options); taskId++; } // media job submission job.Submit(); } catch (Exception e) { log.Info($"Exception {e}"); return(req.CreateResponse(HttpStatusCode.BadRequest)); } // Prepare output JSON int taskIndex = 0; JArray mediaTaskOutputs = new JArray(); foreach (var task in job.Tasks) { JObject o = new JObject(); o["mediaTaskIndex"] = taskIndex; o["mediaTaskId"] = task.Id; o["mediaTaskName"] = task.Name; o["mediaProcessorId"] = task.MediaProcessorId; o["mediaTaskOutputAssetId"] = task.OutputAssets[0].Id; mediaTaskOutputs.Add(o); taskIndex++; } JObject result = new JObject(); result["jobId"] = job.Id; result["mediaTaskOutputs"] = mediaTaskOutputs; return(req.CreateResponse(HttpStatusCode.OK, result)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - CreateTransform was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.transformName == null) { return(new BadRequestObjectResult("Please pass transformName in the input object")); } string transformName = data.transformName; if (data.mode == null) { return(new BadRequestObjectResult("Please pass mode in the input object")); } string description = null; if (data.description != null) { description = data.description; } string mode = data.mode; if (mode != "simple" && mode != "advanced") { return(new BadRequestObjectResult("Please pass valid mode in the input object")); } // // Simple Mode // if (mode == "simple" && data.preset == null) { return(new BadRequestObjectResult("Please pass preset in the input object")); } string presetName = data.preset; if (presetName == "CustomPreset" && data.customPresetJson == null) { return(new BadRequestObjectResult("Please pass customPresetJson in the input object")); } // // Advanced Mode // if (mode == "advanced" && data.transformOutputs == null) { return(new BadRequestObjectResult("Please pass transformOutputs in the input object")); } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); string transformId = null; JsonConverter[] jsonReaders = { new MediaServicesHelperJsonReader(), new MediaServicesHelperTimeSpanJsonConverter() }; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); // Does a Transform already exist with the desired name? // Assume that an existing Transform with the desired name // also uses the same recipe or Preset for processing content. Transform transform = client.Transforms.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName); if (transform == null) { TransformOutput[] outputs = null; List <TransformOutput> transformOutputs = new List <TransformOutput>(); if (mode == "simple") { Preset preset = null; string audioLanguage = null; if (data.audioLanguage != null) { audioLanguage = data.audioLanguage; } if (presetName == "VideoAnalyzer") { bool insightEnabled = false; if (data.insightsToExtract != null && InsightTypeList.ContainsKey(data.insightsToExtract)) { insightEnabled = true; } preset = new VideoAnalyzerPreset(audioLanguage, insightEnabled ? InsightTypeList[data.insightsToExtract] : null); } else if (presetName == "AudioAnalyzer") { preset = new AudioAnalyzerPreset(audioLanguage); } else if (presetName == "CustomPreset") { preset = JsonConvert.DeserializeObject <StandardEncoderPreset>(data.customPresetJson.ToString(), jsonReaders); } else { if (!EncoderNamedPresetList.ContainsKey(presetName)) { return(new BadRequestObjectResult("Preset not found")); } preset = new BuiltInStandardEncoderPreset(EncoderNamedPresetList[presetName]); } OnErrorType onError = OnErrorType.StopProcessingJob; string temp = data.onError; if (!string.IsNullOrEmpty(temp) && OnErrorTypeList.ContainsKey(temp)) { onError = OnErrorTypeList[temp]; } Priority relativePriority = Priority.Normal; temp = data.relativePriority; if (!string.IsNullOrEmpty(temp) && PriorityList.ContainsKey(temp)) { relativePriority = PriorityList[temp]; } transformOutputs.Add(new TransformOutput(preset, onError, relativePriority)); outputs = transformOutputs.ToArray(); } else if (mode == "advanced") { List <TransformOutput> transformOutputList = JsonConvert.DeserializeObject <List <TransformOutput> >(data.transformOutputs.ToString(), jsonReaders); outputs = transformOutputList.ToArray(); } else { return(new BadRequestObjectResult("Invalid mode found")); } // Create Transform transform = client.Transforms.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, transformName, outputs); } transformId = transform.Id; } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { transformName = transformName, transformId = transformId })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - PublishAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.assetName == null) { return(new BadRequestObjectResult("Please pass assetName in the input object")); } if (data.streamingPolicyName == null) { return(new BadRequestObjectResult("Please pass streamingPolicyName in the input object")); } string assetName = data.assetName; string streamingPolicyName = data.streamingPolicyName; string alternativeMediaId = null; if (data.alternativeMediaId != null) { alternativeMediaId = data.alternativeMediaId; } string contentKeyPolicyName = null; if (data.contentKeyPolicyName != null) { contentKeyPolicyName = data.contentKeyPolicyName; } List <StreamingLocatorContentKey> contentKeys = new List <StreamingLocatorContentKey>(); DateTime startDateTime = new DateTime(0); if (data.startDateTime != null) { startDateTime = data.startDateTime; } DateTime endDateTime = new DateTime(0); if (data.endDateTime != null) { endDateTime = data.endDateTime; } Guid streamingLocatorId = Guid.NewGuid(); if (data.streamingLocatorId != null) { streamingLocatorId = new Guid((string)(data.streamingLocatorId)); } string streamingLocatorName = "streaminglocator-" + streamingLocatorId.ToString(); MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); StreamingLocator streamingLocator = null; Asset asset = null; StreamingPolicy streamingPolicy = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); asset = client.Assets.Get(amsconfig.ResourceGroup, amsconfig.AccountName, assetName); if (asset == null) { return(new BadRequestObjectResult("Asset not found")); } streamingPolicy = client.StreamingPolicies.Get(amsconfig.ResourceGroup, amsconfig.AccountName, streamingPolicyName); if (streamingPolicy == null) { return(new BadRequestObjectResult("StreamingPolicy not found")); } if (contentKeyPolicyName != null) { ContentKeyPolicy contentKeyPolicy = null; contentKeyPolicy = client.ContentKeyPolicies.Get(amsconfig.ResourceGroup, amsconfig.AccountName, contentKeyPolicyName); if (contentKeyPolicy == null) { return(new BadRequestObjectResult("ContentKeyPolicy not found")); } } if (data.contentKeys != null) { JsonConverter[] jsonConverters = { new MediaServicesHelperJsonReader() }; contentKeys = JsonConvert.DeserializeObject <List <StreamingLocatorContentKey> >(data.contentKeys.ToString(), jsonConverters); } streamingLocator = new StreamingLocator() { AssetName = assetName, StreamingPolicyName = streamingPolicyName, AlternativeMediaId = alternativeMediaId, DefaultContentKeyPolicyName = contentKeyPolicyName, StartTime = null, EndTime = null, StreamingLocatorId = streamingLocatorId, }; if (!startDateTime.Equals(new DateTime(0))) { streamingLocator.StartTime = startDateTime; } if (!endDateTime.Equals(new DateTime(0))) { streamingLocator.EndTime = endDateTime; } if (contentKeys.Count != 0) { streamingLocator.ContentKeys = contentKeys; } streamingLocator.Validate(); client.StreamingLocators.Create(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName, streamingLocator); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { streamingLocatorName = streamingLocatorName, streamingLocatorId = streamingLocator.StreamingLocatorId.ToString() })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - SubmitMediaJob was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.inputAssetName == null) { return(new BadRequestObjectResult("Please pass inputAssetName in the input object")); } if (data.transformName == null) { return(new BadRequestObjectResult("Please pass transformName in the input object")); } if (data.outputAssetNamePrefix == null) { return(new BadRequestObjectResult("Please pass outputAssetNamePrefix in the input object")); } string inputAssetName = data.inputAssetName; string transformName = data.transformName; string outputAssetNamePrefix = data.outputAssetNamePrefix; string outputAssetDescription = null; if (data.outputAssetDescription != null) { outputAssetDescription = data.outputAssetDescription; } string assetStorageAccount = null; if (data.assetStorageAccount != null) { assetStorageAccount = data.assetStorageAccount; } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); Asset inputAsset = null; string guid = Guid.NewGuid().ToString(); string jobName = "amsv3function-job-" + guid; string encoderOutputAssetName = null; string audioAnalyzerOutputAssetName = null; string videoAnalyzerOutputAssetName = null; JArray outputs = new JArray(); try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); inputAsset = client.Assets.Get(amsconfig.ResourceGroup, amsconfig.AccountName, inputAssetName); if (inputAsset == null) { return(new BadRequestObjectResult("Asset for input not found")); } Transform transform = client.Transforms.Get(amsconfig.ResourceGroup, amsconfig.AccountName, transformName); if (transform == null) { return(new BadRequestObjectResult("Transform not found")); } var jobOutputList = new List <JobOutput>(); for (int i = 0; i < transform.Outputs.Count; i++) { Guid assetGuid = Guid.NewGuid(); string outputAssetName = outputAssetNamePrefix + "-" + assetGuid.ToString(); if (outputAssetDescription == null) { outputAssetDescription = outputAssetName; } Asset assetParams = new Asset(null, outputAssetName, null, assetGuid, DateTime.Now, DateTime.Now, null, outputAssetDescription, null, assetStorageAccount, AssetStorageEncryptionFormat.None); Asset outputAsset = client.Assets.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, outputAssetName, assetParams); jobOutputList.Add(new JobOutputAsset(outputAssetName)); Preset preset = transform.Outputs[i].Preset; if (encoderOutputAssetName == null && (preset is BuiltInStandardEncoderPreset || preset is StandardEncoderPreset)) { encoderOutputAssetName = outputAssetName; } if (audioAnalyzerOutputAssetName == null && preset is AudioAnalyzerPreset) { audioAnalyzerOutputAssetName = outputAssetName; } if (videoAnalyzerOutputAssetName == null && preset is VideoAnalyzerPreset) { videoAnalyzerOutputAssetName = outputAssetName; } // set up jobOutputs JObject outObject = new JObject(); outObject["Preset"] = preset.ToString().Split('.').Last <string>(); outObject["OutputAssetName"] = outputAssetName; if (preset is BuiltInStandardEncoderPreset || preset is StandardEncoderPreset) { outObject["Category"] = "Encoder"; } else if (preset is AudioAnalyzerPreset || preset is VideoAnalyzerPreset) { outObject["Category"] = "Analyzer"; } else { outObject["Category"] = "Unknown"; } outputs.Add(outObject); } // Use the name of the created input asset to create the job input. JobInput jobInput = new JobInputAsset(assetName: inputAssetName); JobOutput[] jobOutputs = jobOutputList.ToArray(); Job job = client.Jobs.Create( amsconfig.ResourceGroup, amsconfig.AccountName, transformName, jobName, new Job { Input = jobInput, Outputs = jobOutputs, } ); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } JObject result = new JObject(); result["jobName"] = jobName; result["jobOutputs"] = outputs; result["encoderOutputAssetName"] = encoderOutputAssetName; result["videoAnalyzerOutputAssetName"] = videoAnalyzerOutputAssetName; result["audioAnalyzerOutputAssetName"] = audioAnalyzerOutputAssetName; return((ActionResult) new OkObjectResult(result)); }
public static IActionResult Run([HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, TraceWriter log) { log.Info($"AMS v3 Function - MonitorBlobContainerCopyStatus was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.assetName == null) { return(new BadRequestObjectResult("Please pass assetName in the input object")); } string assetName = data.assetName; List <string> fileNames = null; if (data.fileNames != null) { fileNames = ((JArray)data.fileNames).ToObject <List <string> >(); } bool copyStatus = true; JArray blobCopyStatusList = new JArray(); MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); Asset asset = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); // Get the Asset asset = client.Assets.Get(amsconfig.ResourceGroup, amsconfig.AccountName, assetName); if (asset == null) { return(new BadRequestObjectResult("Asset not found")); } // Setup blob container var response = client.Assets.ListContainerSas(amsconfig.ResourceGroup, amsconfig.AccountName, assetName, permissions: AssetContainerPermission.Read, expiryTime: DateTime.UtcNow.AddHours(4).ToUniversalTime()); var sasUri = new Uri(response.AssetContainerSasUrls.First()); CloudBlobContainer destinationBlobContainer = new CloudBlobContainer(sasUri); //string blobPrefix = null; //bool useFlatBlobListing = true; //BlobContinuationToken blobContinuationToken = null; log.Info("Checking CopyStatus of all blobs in the source container..."); var blobList = BlobStorageHelper.ListBlobs(destinationBlobContainer); foreach (var blob in blobList) { if (fileNames != null) { bool found = false; foreach (var fileName in fileNames) { if (fileName == blob.Name) { found = true; break; } } if (found == false) { break; } } if (blob.CopyState.Status == CopyStatus.Aborted || blob.CopyState.Status == CopyStatus.Failed) { // Log the copy status description for diagnostics and restart copy blob.StartCopyAsync(blob.CopyState.Source); copyStatus = false; } else if (blob.CopyState.Status == CopyStatus.Pending) { // We need to continue waiting for this pending copy // However, let us log copy state for diagnostics copyStatus = false; } // else we completed this pending copy string blobName = blob.Name as string; int blobCopyStatus = (int)(blob.CopyState.Status); JObject o = new JObject(); o["blobName"] = blobName; o["blobCopyStatus"] = blobCopyStatus; blobCopyStatusList.Add(o); } } catch (ApiErrorException e) { log.Info($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.Info($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } JObject result = new JObject(); result["copyStatus"] = copyStatus; result["blobCopyStatusList"] = blobCopyStatusList; return((ActionResult) new OkObjectResult(result)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - StartBlobContainerCopyToAsset was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.assetName == null) { return(new BadRequestObjectResult("Please pass assetName in the input object")); } if (data.sourceStorageAccountName == null) { return(new BadRequestObjectResult("Please pass sourceStorageAccountName in the input object")); } if (data.sourceStorageAccountKey == null) { return(new BadRequestObjectResult("Please pass sourceStorageAccountKey in the input object")); } if (data.sourceContainer == null) { return(new BadRequestObjectResult("Please pass sourceContainer in the input object")); } string assetName = data.assetName; string sourceStorageAccountName = data.sourceStorageAccountName; string sourceStorageAccountKey = data.sourceStorageAccountKey; string sourceContainerName = data.sourceContainer; List <string> fileNames = null; if (data.fileNames != null) { fileNames = ((JArray)data.fileNames).ToObject <List <string> >(); } string destinationBlobContainerName = null; MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); Asset asset = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); // Get the Asset asset = client.Assets.Get(amsconfig.ResourceGroup, amsconfig.AccountName, assetName); if (asset == null) { return(new BadRequestObjectResult("Asset not found")); } // Setup blob container CloudBlobContainer sourceBlobContainer = BlobStorageHelper.GetCloudBlobContainer(sourceStorageAccountName, sourceStorageAccountKey, sourceContainerName); var response = client.Assets.ListContainerSas(amsconfig.ResourceGroup, amsconfig.AccountName, assetName, permissions: AssetContainerPermission.ReadWrite, expiryTime: DateTime.UtcNow.AddHours(4).ToUniversalTime()); var sasUri = new Uri(response.AssetContainerSasUrls.First()); CloudBlobContainer destinationBlobContainer = new CloudBlobContainer(sasUri); destinationBlobContainerName = destinationBlobContainer.Name; // Copy Source Blob container into Destination Blob container that is associated with the asset. BlobStorageHelper.CopyBlobsAsync(sourceBlobContainer, destinationBlobContainer, fileNames); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { destinationContainer = destinationBlobContainerName })); }
private static IContentKey CreateDummyContentKey(ContentKeyType keyType) { return(MediaServicesHelper.CreateContentKey(_context, "Dummy ContentKey", keyType)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - CreateJwtToken was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); string streaminglocatorName = data.streamingLocatorName; string contentKeyPolicyName = data.contentKeyPolicyName; string accountName = data.accountName; string resourceGroup = data.resourceGroup; MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); string token = null; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); var response = client.StreamingLocators.ListContentKeys(resourceGroup, accountName, streaminglocatorName); var contentKeyId = response.ContentKeys.First().Id.ToString(); var policyProperties = client.ContentKeyPolicies.GetPolicyPropertiesWithSecrets(resourceGroup, accountName, contentKeyPolicyName); var ckrestriction = (ContentKeyPolicyTokenRestriction)policyProperties.Options.FirstOrDefault()?.Restriction; var symKey = (ContentKeyPolicySymmetricTokenKey)ckrestriction.PrimaryVerificationKey; var tokenSigningKey = new SymmetricSecurityKey(symKey.KeyValue); SigningCredentials cred = new SigningCredentials( tokenSigningKey, // Use the HmacSha256 and not the HmacSha256Signature option, or the token will not work! SecurityAlgorithms.HmacSha256, SecurityAlgorithms.Sha256Digest); Claim[] claims = new Claim[] { new Claim(ContentKeyPolicyTokenClaim.ContentKeyIdentifierClaim.ClaimType, contentKeyId) }; JwtSecurityToken jwtToken = new JwtSecurityToken( issuer: ckrestriction.Issuer, audience: ckrestriction.Audience, claims: claims, notBefore: DateTime.Now.AddMinutes(-5), expires: DateTime.Now.AddMinutes(60), signingCredentials: cred); JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler(); token = $"Bearer {handler.WriteToken(jwtToken)}"; } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { token = token })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - CreateContentKeyPolicy was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); if (data.contentKeyPolicyName == null) { return(new BadRequestObjectResult("Please pass contentKeyPolicyName in the input object")); } string contentKeyPolicyName = data.contentKeyPolicyName; string contentKeyPolicyDescription = null; if (data.contentKeyPolicyDescription == null) { contentKeyPolicyDescription = data.contentKeyPolicyDescription; } string mode = data.mode; if (mode != "simple" && mode != "advanced") { return(new BadRequestObjectResult("Please pass valid mode in the input object")); } // // Simple Mode // if (mode == "simple") { if (data.policyOptionName == null) { return(new BadRequestObjectResult("Please pass policyOptionName in the input object")); } if (data.openRestriction == null) { return(new BadRequestObjectResult("Please pass openRestriction in the input object")); } if (data.openRestriction == false) { if (data.audience == null && data.issuer == null && data.tokenType == null && data.tokenKeyType == null && data.tokenKey == null) { return(new BadRequestObjectResult("Please pass required parameters for Token Restriction in the input object")); } } } // // Advanced Mode // if (mode == "advanced") { if (data.contentKeyPolicyOptions == null) { return(new BadRequestObjectResult("Please pass contentKeyPolicyOptions in the input object")); } } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); ContentKeyPolicy policy = null; JsonConverter[] jsonReaders = { new MediaServicesHelperJsonReader(), new MediaServicesHelperTimeSpanJsonConverter() }; try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); policy = client.ContentKeyPolicies.Get(amsconfig.ResourceGroup, amsconfig.AccountName, contentKeyPolicyName); if (policy == null) { List <ContentKeyPolicyOption> options = new List <ContentKeyPolicyOption>(); if (mode == "simple") { ContentKeyPolicyOption option = new ContentKeyPolicyOption(); option.Name = data.policyOptionName; // Restrictions if ((bool)data.openRestriction) { option.Restriction = new ContentKeyPolicyOpenRestriction(); } else { ContentKeyPolicyTokenRestriction restriction = new ContentKeyPolicyTokenRestriction(); restriction.Audience = data.audience; restriction.Issuer = data.issuer; string tokenType = data.tokenType; switch (tokenType) { case "Jwt": restriction.RestrictionTokenType = ContentKeyPolicyRestrictionTokenType.Jwt; break; case "Swt": restriction.RestrictionTokenType = ContentKeyPolicyRestrictionTokenType.Swt; break; default: return(new BadRequestObjectResult("Please pass valid tokenType in the input object")); } string tokenKeyType = data.tokenKeyType; switch (tokenKeyType) { case "Symmetric": restriction.PrimaryVerificationKey = new ContentKeyPolicySymmetricTokenKey(Convert.FromBase64String(data.tokenKey.ToString())); break; case "X509": restriction.PrimaryVerificationKey = new ContentKeyPolicyX509CertificateTokenKey(Convert.FromBase64String(data.tokenKey.ToString())); break; case "RSA": restriction.PrimaryVerificationKey = new ContentKeyPolicyRsaTokenKey(); break; default: return(new BadRequestObjectResult("Please pass valid tokenKeyType in the input object")); } if (data.tokenClaims != null) { restriction.RequiredClaims = new List <ContentKeyPolicyTokenClaim>(); String[] tokenClaims = data.tokenClaims.ToString().Split(';'); foreach (string tokenClaim in tokenClaims) { String[] tokenClaimKVP = tokenClaim.Split('='); if (tokenClaimKVP.Length == 2) { restriction.RequiredClaims.Add(new ContentKeyPolicyTokenClaim(tokenClaimKVP[0], tokenClaimKVP[1] == "null" ? null : tokenClaimKVP[1])); } else if (tokenClaimKVP.Length == 1) { restriction.RequiredClaims.Add(new ContentKeyPolicyTokenClaim(tokenClaimKVP[0])); } else { return(new BadRequestObjectResult("Please pass valid tokenClaims in the input object")); } } } if (data.openIdConnectDiscoveryDocument != null) { restriction.OpenIdConnectDiscoveryDocument = data.openIdConnectDiscoveryDocument; } option.Restriction = restriction; } // Configuration string configurationType = data.configurationType; switch (configurationType) { case "ClearKey": option.Configuration = new ContentKeyPolicyClearKeyConfiguration(); break; case "FairPlay": ContentKeyPolicyFairPlayConfiguration configFairPlay = new ContentKeyPolicyFairPlayConfiguration(); configFairPlay.Ask = Convert.FromBase64String(data.fairPlayAsk); configFairPlay.FairPlayPfx = data.fairPlayPfx; configFairPlay.FairPlayPfxPassword = data.fairPlayPfxPassword; switch (data.faiPlayRentalAndLeaseKeyType) { case "Undefined": configFairPlay.RentalAndLeaseKeyType = ContentKeyPolicyFairPlayRentalAndLeaseKeyType.Undefined; break; case "PersistentLimited": configFairPlay.RentalAndLeaseKeyType = ContentKeyPolicyFairPlayRentalAndLeaseKeyType.PersistentLimited; break; case "PersistentUnlimited": configFairPlay.RentalAndLeaseKeyType = ContentKeyPolicyFairPlayRentalAndLeaseKeyType.PersistentUnlimited; break; default: return(new BadRequestObjectResult("Please pass valid faiPlayRentalAndLeaseKeyType in the input object")); } configFairPlay.RentalDuration = data.faiPlayRentalDuration; break; case "PlayReady": ContentKeyPolicyPlayReadyConfiguration configPlayReady = new ContentKeyPolicyPlayReadyConfiguration(); configPlayReady.Licenses = JsonConvert.DeserializeObject <List <ContentKeyPolicyPlayReadyLicense> >(data.playReadyTemplates.ToString(), jsonReaders); if (data.playReadyResponseCustomData != null) { configPlayReady.ResponseCustomData = data.playReadyResponseCustomData; } option.Configuration = configPlayReady; break; case "Widevine": ContentKeyPolicyWidevineConfiguration configWideVine = JsonConvert.DeserializeObject <ContentKeyPolicyWidevineConfiguration>(data.widevineTemplate.ToString(), jsonReaders); option.Configuration = configWideVine; break; default: return(new BadRequestObjectResult("Please pass valid configurationType in the input object")); } options.Add(option); } else if (mode == "advanced") { options = JsonConvert.DeserializeObject <List <ContentKeyPolicyOption> >(data.contentKeyPolicyOptions.ToString(), jsonReaders); } foreach (ContentKeyPolicyOption o in options) { o.Validate(); } policy = client.ContentKeyPolicies.CreateOrUpdate(amsconfig.ResourceGroup, amsconfig.AccountName, contentKeyPolicyName, options, contentKeyPolicyDescription); } } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } return((ActionResult) new OkObjectResult(new { policyId = policy.PolicyId })); }
public static async Task <object> Run([HttpTrigger(WebHookType = "genericJson")] HttpRequestMessage req, TraceWriter log) { log.Info($"AMS v2 Function - GetCaptionBlobSasUri was triggered!"); string jsonContent = await req.Content.ReadAsStringAsync(); dynamic data = JsonConvert.DeserializeObject(jsonContent); // parameter handling if (data.assetId == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Please pass assetId in the input object" })); } string assetId = data.assetId; string timecodeOffset = null; if (data.timecodeOffset != null) { timecodeOffset = data.timecodeOffset; } MediaServicesCredentials amsCredentials = new MediaServicesCredentials(); IAsset asset = null; string vttContent = ""; string vttContentTimeOffset = ""; string vttBlobSasUri = null; string ttmlContent = ""; string ttmlContentTimeOffset = ""; string ttmlBlobSasUri = null; try { // Load AMS account context log.Info($"Using AMS v2 REST API Endpoint : {amsCredentials.AmsRestApiEndpoint.ToString()}"); AzureAdTokenCredentials tokenCredentials = new AzureAdTokenCredentials(amsCredentials.AmsAadTenantDomain, new AzureAdClientSymmetricKey(amsCredentials.AmsClientId, amsCredentials.AmsClientSecret), AzureEnvironments.AzureCloudEnvironment); AzureAdTokenProvider tokenProvider = new AzureAdTokenProvider(tokenCredentials); _context = new CloudMediaContext(amsCredentials.AmsRestApiEndpoint, tokenProvider); // Get the Asset asset = _context.Assets.Where(a => a.Id == assetId).FirstOrDefault(); if (asset == null) { return(req.CreateResponse(HttpStatusCode.BadRequest, new { error = "Asset not found" })); } string destinationContainerName = asset.Uri.Segments[1]; var vttAssetFile = asset.AssetFiles.Where(a => a.Name.ToUpper().EndsWith(".VTT")).FirstOrDefault(); var ttmlAssetFile = asset.AssetFiles.Where(a => a.Name.ToUpper().EndsWith(".TTML")).FirstOrDefault(); if (vttAssetFile != null) { vttContent = MediaServicesHelper.GetContentFromAssetFile(vttAssetFile); CloudBlobContainer destinationBlobContainer = BlobStorageHelper.GetCloudBlobContainer(BlobStorageHelper.AmsStorageAccountName, BlobStorageHelper.AmsStorageAccountKey, destinationContainerName); CloudBlockBlob blobVTT = destinationBlobContainer.GetBlockBlobReference(vttAssetFile.Name); if (timecodeOffset != null) // let's update the ttml with new timecode { var tcOffset = TimeSpan.Parse(timecodeOffset); string arrow = " --> "; System.Text.StringBuilder sb = new System.Text.StringBuilder(); string[] delim = { Environment.NewLine, "\n" }; // "\n" added in case you manually appended a newline string[] vttlines = vttContent.Split(delim, StringSplitOptions.None); foreach (string vttline in vttlines) { string line = vttline; if (vttline.Contains(arrow)) { TimeSpan begin = TimeSpan.Parse(vttline.Substring(0, vttline.IndexOf(arrow))); TimeSpan end = TimeSpan.Parse(vttline.Substring(vttline.IndexOf(arrow) + 5)); line = (begin + tcOffset).ToString(@"d\.hh\:mm\:ss\.fff") + arrow + (end + tcOffset).ToString(@"d\.hh\:mm\:ss\.fff"); } sb.AppendLine(line); } vttContentTimeOffset = sb.ToString(); if (vttContentTimeOffset != null) { string blobName = "Converted-" + vttAssetFile.Name; blobVTT = MediaServicesHelper.WriteContentToBlob(asset, destinationBlobContainer, blobName, vttContentTimeOffset); } } // Get Blob URI with SAS Token var sasBlobPolicy = new SharedAccessBlobPolicy(); sasBlobPolicy.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5); sasBlobPolicy.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10); sasBlobPolicy.Permissions = SharedAccessBlobPermissions.Read; string sasBlobToken = blobVTT.GetSharedAccessSignature(sasBlobPolicy); vttBlobSasUri = blobVTT.Uri + sasBlobToken; } if (ttmlAssetFile != null) { ttmlContent = MediaServicesHelper.GetContentFromAssetFile(ttmlAssetFile); CloudBlobContainer destinationBlobContainer = BlobStorageHelper.GetCloudBlobContainer(BlobStorageHelper.AmsStorageAccountName, BlobStorageHelper.AmsStorageAccountKey, destinationContainerName); CloudBlockBlob blobTTML = destinationBlobContainer.GetBlockBlobReference(ttmlAssetFile.Name); if (timecodeOffset != null) // let's update the vtt with new timecode { var tcOffset = TimeSpan.Parse(timecodeOffset); //log.Info("tsoffset : " + tcOffset.ToString(@"d\.hh\:mm\:ss\.fff")); XNamespace xmlns = "http://www.w3.org/ns/ttml"; XDocument docXML = XDocument.Parse(ttmlContent); var tt = docXML.Element(xmlns + "tt"); var ttmlElements = docXML.Element(xmlns + "tt").Element(xmlns + "body").Element(xmlns + "div").Elements(xmlns + "p"); foreach (var ttmlElement in ttmlElements) { var begin = TimeSpan.Parse((string)ttmlElement.Attribute("begin")); var end = TimeSpan.Parse((string)ttmlElement.Attribute("end")); ttmlElement.SetAttributeValue("end", (end + tcOffset).ToString(@"d\.hh\:mm\:ss\.fff")); ttmlElement.SetAttributeValue("begin", (begin + tcOffset).ToString(@"d\.hh\:mm\:ss\.fff")); } ttmlContentTimeOffset = docXML.Declaration.ToString() + Environment.NewLine + docXML.ToString(); if (ttmlContentTimeOffset != null) { string blobName = "Converted-" + ttmlAssetFile.Name; blobTTML = MediaServicesHelper.WriteContentToBlob(asset, destinationBlobContainer, blobName, ttmlContentTimeOffset); } } // Get Blob URI with SAS Token var sasBlobPolicy = new SharedAccessBlobPolicy(); sasBlobPolicy.SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5); sasBlobPolicy.SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(10); sasBlobPolicy.Permissions = SharedAccessBlobPermissions.Read; string sasBlobToken = blobTTML.GetSharedAccessSignature(sasBlobPolicy); ttmlBlobSasUri = blobTTML.Uri + sasBlobToken; } } catch (Exception e) { log.Info($"Exception {e}"); return(req.CreateResponse(HttpStatusCode.BadRequest)); } return(req.CreateResponse(HttpStatusCode.OK, new { vttBlobSasUri = vttBlobSasUri, ttmlBlobSasUri = ttmlBlobSasUri })); }
/// <summary> /// Submits job to Azure Media Services. /// </summary> /// <param name="jobRequestModel">Job to submit.</param> /// <param name="logger">Logger to log data</param> /// <returns>Submitted job</returns> public async Task <Job> SubmitJobAsync(JobRequestModel jobRequestModel, ILogger logger) { logger.LogInformation($"JobSchedulingService::SubmitJobAsync started: jobRequestModel={LogHelper.FormatObjectForLog(jobRequestModel)}"); // Get next available Azure Media Services instance var selectedInstanceName = await this.mediaServiceInstanceHealthService.GetNextAvailableInstanceAsync(logger).ConfigureAwait(false); logger.LogInformation($"JobSchedulingService::SubmitJobAsync selected healthy instance: MediaServiceAccountName={selectedInstanceName} jobRequestModel={LogHelper.FormatObjectForLog(jobRequestModel)}"); // load configuration for specific instance var clientConfiguration = this.configService.MediaServiceInstanceConfiguration[selectedInstanceName]; // get client var clientInstance = this.mediaServiceInstanceFactory.GetMediaServiceInstance(selectedInstanceName, logger); // In order to submit a new job, output asset has to be created first var asset = await clientInstance.Assets.CreateOrUpdateAsync( clientConfiguration.ResourceGroup, clientConfiguration.AccountName, jobRequestModel.OutputAssetName, new Asset()).ConfigureAwait(false); JobOutput[] jobOutputs = { new JobOutputAsset(jobRequestModel.OutputAssetName) }; // submit new job var job = await clientInstance.Jobs.CreateAsync( clientConfiguration.ResourceGroup, clientConfiguration.AccountName, jobRequestModel.TransformName, jobRequestModel.JobName, new Job { Input = jobRequestModel.JobInputs, Outputs = jobOutputs, }).ConfigureAwait(false); logger.LogInformation($"JobSchedulingService::SubmitJobAsync successfully created job: job={LogHelper.FormatObjectForLog(job)}"); // create job verification request var jobVerificationRequestModel = new JobVerificationRequestModel { Id = Guid.NewGuid().ToString(), JobId = job.Id, OriginalJobRequestModel = jobRequestModel, MediaServiceAccountName = selectedInstanceName, JobOutputAssetName = jobRequestModel.OutputAssetName, JobName = job.Name, RetryCount = 0 // initial submission, only certain number of retries are performed before skipping job verification retry, // see job verification service for more details }; //create job output status record var statusInfo = MediaServicesHelper.GetJobOutputState(job, jobRequestModel.OutputAssetName); var jobOutputStatusModel = new JobOutputStatusModel { Id = Guid.NewGuid().ToString(), EventTime = statusInfo.Item2, JobOutputState = statusInfo.Item1, JobName = job.Name, MediaServiceAccountName = selectedInstanceName, JobOutputAssetName = jobRequestModel.OutputAssetName, TransformName = jobRequestModel.TransformName }; // in order to round robin among all healthy services, health service needs to know which instance has been used last // data is persisted in memory only for current process this.mediaServiceInstanceHealthService.RecordInstanceUsage(selectedInstanceName, logger); var retryCount = 3; var retryTimeOut = 1000; // Job is submitted at this point, failing to do any calls after this point would result in reprocessing this job request and submitting duplicate one. // It is OK to retry and ignore exception at the end. In current implementation based on Azure storage, it is very unlikely to fail in any of the below calls. do { try { // persist initial job output status await this.jobOutputStatusStorageService.CreateOrUpdateAsync(jobOutputStatusModel, logger).ConfigureAwait(false); // persist job verification request. It is used to trigger logic to verify that job was completed and not stuck sometime in future. var jobVerificationResult = await this.jobVerificationRequestStorageService.CreateAsync(jobVerificationRequestModel, this.verificationDelay, logger).ConfigureAwait(false); logger.LogInformation($"JobSchedulingService::SubmitJobAsync successfully submitted jobVerificationModel: result={LogHelper.FormatObjectForLog(jobVerificationResult)}"); // no exception happened, let's break. break; } #pragma warning disable CA1031 // Do not catch general exception types catch (Exception e) #pragma warning restore CA1031 // Do not catch general exception types { logger.LogError($"JobSchedulingService::SubmitJobAsync got exception calling jobVerificationRequestStorageService.CreateAsync: retryCount={retryCount} message={e.Message} job={LogHelper.FormatObjectForLog(job)}"); retryCount--; await Task.Delay(retryTimeOut).ConfigureAwait(false); } }while (retryCount > 0); logger.LogInformation($"JobSchedulingService::SubmitJobAsync completed: job={LogHelper.FormatObjectForLog(job)}"); return(job); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - GetAssetUrls was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Validate input objects if (data.streamingLocatorName == null) { return(new BadRequestObjectResult("Please pass streamingLocatorName in the input object")); } string streamingLocatorName = data.streamingLocatorName; string streamingEndpointName = "default"; if (data.streamingEndpointName != null) { streamingEndpointName = data.streamingEndpointName; } string streamingUrlScheme = "https"; if (data.streamingUrlScheme != null) { if (data.streamingUrlScheme.ToString().Equals("http") || data.streamingUrlScheme.ToString().Equals("https")) { streamingUrlScheme = data.streamingUrlScheme; } else { return(new BadRequestObjectResult("Please pass streamingLocatorName in the input object")); } } MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); JArray downloadPaths = new JArray(); JArray streamingPaths = new JArray(); try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); var paths = client.StreamingLocators.ListPaths(amsconfig.ResourceGroup, amsconfig.AccountName, streamingLocatorName); var streamingEndpoint = client.StreamingEndpoints.Get(amsconfig.ResourceGroup, amsconfig.AccountName, streamingEndpointName); foreach (var path in paths.DownloadPaths) { UriBuilder uriBuilder = new UriBuilder(); uriBuilder.Scheme = streamingUrlScheme; uriBuilder.Host = streamingEndpoint.HostName; uriBuilder.Path = path; downloadPaths.Add(uriBuilder.ToString()); } foreach (var path in paths.StreamingPaths) { UriBuilder uriBuilder = new UriBuilder(); uriBuilder.Scheme = streamingUrlScheme; uriBuilder.Host = streamingEndpoint.HostName; if (path.Paths.Count > 0) { uriBuilder.Path = path.Paths[0]; JObject p = new JObject(); p["StreamingProtocol"] = path.StreamingProtocol.ToString(); p["EncryptionScheme"] = path.EncryptionScheme.ToString(); p["StreamingUrl"] = uriBuilder.ToString(); streamingPaths.Add(p); } } } catch (ApiErrorException e) { log.LogInformation($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogInformation($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message)); } JObject result = new JObject(); result["downloadPaths"] = downloadPaths; result["streamingPaths"] = streamingPaths; return((ActionResult) new OkObjectResult(result)); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation($"AMS v3 Function - CopyUrlToStorage was triggered!"); string requestBody = new StreamReader(req.Body).ReadToEnd(); dynamic data = JsonConvert.DeserializeObject(requestBody); // Set up ENV variables string accountName = System.Environment.GetEnvironmentVariable("AccountName"); string resourceGroup = System.Environment.GetEnvironmentVariable("ResourceGroup"); MediaServicesConfigWrapper amsconfig = new MediaServicesConfigWrapper(); // Transform & Job string transformName = "abrTransform"; string jobName = "job-" + Guid.NewGuid(); // Asset & Url string remoteUrl = Convert.ToString(data.remoteUrl); string assetName = Convert.ToString(data.assetName); try { IAzureMediaServicesClient client = MediaServicesHelper.CreateMediaServicesClientAsync(amsconfig); // Encode from any HTTPs source URL - a new feature of the v3 API. JobInputHttp jobInput = new JobInputHttp(files: new List <string> { remoteUrl }); JobOutput[] jobOutputs = { new JobOutputAsset(assetName), }; // In this function, we are ensuring that the job name is unique. Job job = await client.Jobs.CreateAsync( resourceGroup, accountName, transformName, jobName, new Job { Input = jobInput, Outputs = jobOutputs, }); } catch (ApiErrorException e) { log.LogError($"ERROR: AMS API call failed with error code: {e.Body.Error.Code} and message: {e.Body.Error.Message}"); return(new BadRequestObjectResult("AMS API call error: " + e.Message + "\nError Code: " + e.Body.Error.Code + "\nMessage: " + e.Body.Error.Message)); } catch (Exception e) { log.LogError($"ERROR: Exception with message: {e.Message}"); return(new BadRequestObjectResult("Error: " + e.Message + " caused by " + e.StackTrace)); } return((ActionResult) new OkObjectResult(new { assetName = assetName })); }