/// <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);
        }
Beispiel #2
0
        /// <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
            }));
        }
Beispiel #4
0
        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
            }));
        }
Beispiel #5
0
        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
            }));
        }
Beispiel #7
0
        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));
            }
        }
Beispiel #8
0
        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}");
        }
Beispiel #9
0
        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
            }));
        }
Beispiel #11
0
        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
            }));
        }
Beispiel #13
0
        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));
        }
Beispiel #14
0
        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()
            }));
        }
Beispiel #20
0
        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));
        }
Beispiel #21
0
        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));
        }
Beispiel #22
0
        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));
 }
Beispiel #24
0
        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
            }));
        }
Beispiel #25
0
        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
            }));
        }
Beispiel #27
0
        /// <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);
        }
Beispiel #28
0
        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));
        }
Beispiel #29
0
        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
            }));
        }