private BMEssence CreateBmEssence(BMContent bmContent, S3Locator location, JToken mediaInfo) { return(new BMEssence { BmContent = bmContent.Id, Locations = new Locator[] { location }, ["technicalMetadata"] = mediaInfo }); }
public static async Task ProcessRekognitionResultAsync(AwsAiServiceWorkerRequest @event) { var resourceManager = @event.GetAwsV4ResourceManager(); var table = new DynamoDbTable(@event.StageVariables["TableName"]); var jobAssignmentId = @event.JobAssignmentId; var job = await RetrieveJobAsync(resourceManager, table, jobAssignmentId); try { var jobInput = job.JobInput; S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var s3Bucket = outputLocation.AwsS3Bucket; var rekoJobId = @event.JobExternalInfo.RekoJobId; var rekoJobType = @event.JobExternalInfo.RekoJobType; var status = @event.JobExternalInfo.Status; if (status != "SUCCEEDED") { throw new Exception($"AI Rekognition failed job info: rekognition status:" + status); } object data = null; switch (rekoJobType) { case "StartCelebrityRecognition": var getCelebRecognitionParams = new GetCelebrityRecognitionRequest { JobId = rekoJobId, /* required */ MaxResults = 1000000, SortBy = "TIMESTAMP" }; var rekognitionClient = new AmazonRekognitionClient(); data = await rekognitionClient.GetCelebrityRecognitionAsync(getCelebRecognitionParams); break; case "StartLabelDetection": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); case "StartContentModeration": throw new NotImplementedException("StartContentModeration has not yet been implemented"); case "StartPersonTracking": throw new NotImplementedException("StartPersonTracking has not yet been implemented"); case "StartFaceDetection": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); case "StartFaceSearch": throw new NotImplementedException("StartLabelDetection has not yet been implemented"); default: throw new Exception($"Unknown job type '{rekoJobType}'"); } if (data == null) { throw new Exception($"No data was returned by AWS Rekognition"); } var newS3Key = $"reko_{Guid.NewGuid()}.json"; var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = newS3Key, ContentBody = data.ToMcmaJson().ToString(), ContentType = "application/json" }; try { var destS3 = await outputLocation.GetClientAsync(); await destS3.PutObjectAsync(s3Params); } catch (Exception error) { Logger.Error("Unable to write output file to bucket '" + s3Bucket + "' with key '" + newS3Key + "'"); Logger.Exception(error); } var jobOutput = new JobParameterBag(); jobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = newS3Key }; await UpdateJobAssignmentWithOutputAsync(table, jobAssignmentId, jobOutput); await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "COMPLETED"); } catch (Exception error) { Logger.Exception(error); try { await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "FAILED", error.ToString()); } catch (Exception innerError) { Logger.Exception(innerError); } } }
internal static async Task ProcessJobAssignmentAsync(AwsAiServiceWorkerRequest @event) { var resourceManager = @event.GetAwsV4ResourceManager(); var table = new DynamoDbTable(@event.StageVariables["TableName"]); var jobAssignmentId = @event.JobAssignmentId; try { // 1. Setting job assignment status to RUNNING await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "RUNNING", null); // 2. Retrieving WorkflowJob var job = await RetrieveJobAsync(resourceManager, table, jobAssignmentId); // 3. Retrieve JobProfile var jobProfile = await RetrieveJobProfileAsync(resourceManager, job); // 4. Retrieve job inputParameters var jobInput = job.JobInput; // 5. Check if we support jobProfile and if we have required parameters in jobInput ValidateJobProfile(jobProfile, jobInput); S3Locator inputFile; if (!jobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception("Invalid or missing input file."); } S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } switch (jobProfile.Name) { case JOB_PROFILE_TRANSCRIBE_AUDIO: string mediaFileUrl; if (!string.IsNullOrWhiteSpace(inputFile.HttpEndpoint)) { mediaFileUrl = inputFile.HttpEndpoint; } else { var bucketLocation = await inputFile.GetBucketLocationAsync(); var s3SubDomain = !string.IsNullOrWhiteSpace(bucketLocation) ? $"s3-{bucketLocation}" : "s3"; mediaFileUrl = $"https://{s3SubDomain}.amazonaws.com/{inputFile.AwsS3Bucket}/{inputFile.AwsS3Key}"; } string mediaFormat; if (mediaFileUrl.EndsWith("mp3", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "mp3"; } else if (mediaFileUrl.EndsWith("mp4", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "mp4"; } else if (mediaFileUrl.EndsWith("wav", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "wav"; } else if (mediaFileUrl.EndsWith("flac", StringComparison.OrdinalIgnoreCase)) { mediaFormat = "flac"; } else { throw new Exception($"Unable to determine media format from input file '{mediaFileUrl}'"); } var transcribeParameters = new StartTranscriptionJobRequest { TranscriptionJobName = "TranscriptionJob-" + jobAssignmentId.Substring(jobAssignmentId.LastIndexOf("/") + 1), LanguageCode = "en-US", Media = new Media { MediaFileUri = mediaFileUrl }, MediaFormat = mediaFormat, OutputBucketName = @event.StageVariables["ServiceOutputBucket"] }; var transcribeService = new AmazonTranscribeServiceClient(); var startJobResponse = await transcribeService.StartTranscriptionJobAsync(transcribeParameters); Logger.Debug(startJobResponse.ToMcmaJson().ToString()); break; case JOB_PROFILE_TRANSLATE_TEXT: var s3Bucket = inputFile.AwsS3Bucket; var s3Key = inputFile.AwsS3Key; GetObjectResponse s3Object; try { s3Object = await inputFile.GetAsync(); } catch (Exception error) { throw new Exception($"Unable to read file in bucket '{s3Bucket}' with key '{s3Key}'.", error); } var inputText = await new StreamReader(s3Object.ResponseStream).ReadToEndAsync(); var translateParameters = new TranslateTextRequest { SourceLanguageCode = jobInput.TryGet("sourceLanguageCode", out string srcLanguageCode) ? srcLanguageCode : "auto", TargetLanguageCode = jobInput.Get <string>("targetLanguageCode"), Text = inputText }; var translateService = new AmazonTranslateClient(); var translateResponse = await translateService.TranslateTextAsync(translateParameters); var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = (!string.IsNullOrWhiteSpace(outputLocation.AwsS3KeyPrefix) ? outputLocation.AwsS3Key : string.Empty) + Guid.NewGuid() + ".txt", ContentBody = translateResponse.TranslatedText }; var outputS3 = await outputLocation.GetClientAsync(); await outputS3.PutObjectAsync(s3Params); var jobOutput = new JobParameterBag(); jobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }; Logger.Debug("Updating job assignment"); await UpdateJobAssignmentWithOutputAsync(table, jobAssignmentId, jobOutput); await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "COMPLETED"); break; case JOB_PROFILE_DETECT_CELEBRITIES: var randomBytes = new byte[16]; new Random().NextBytes(randomBytes); var clientToken = randomBytes.HexEncode(); var base64JobId = Encoding.UTF8.GetBytes(jobAssignmentId).HexEncode(); var rekoParams = new StartCelebrityRecognitionRequest { Video = new Video { S3Object = new Amazon.Rekognition.Model.S3Object { Bucket = inputFile.AwsS3Bucket, Name = inputFile.AwsS3Key } }, ClientRequestToken = clientToken, JobTag = base64JobId, NotificationChannel = new NotificationChannel { RoleArn = REKO_SNS_ROLE_ARN, SNSTopicArn = SNS_TOPIC_ARN } }; var rekognitionClient = new AmazonRekognitionClient(); var startCelebRecognitionResponse = await rekognitionClient.StartCelebrityRecognitionAsync(rekoParams); Logger.Debug(startCelebRecognitionResponse.ToMcmaJson().ToString()); break; } } catch (Exception ex) { Logger.Exception(ex); try { await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "FAILED", ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } }
public static async Task ProcessTranscribeJobResultAsync(AwsAiServiceWorkerRequest @event) { var resourceManager = @event.GetAwsV4ResourceManager(); var table = new DynamoDbTable(@event.StageVariables["TableName"]); var jobAssignmentId = @event.JobAssignmentId; var job = await RetrieveJobAsync(resourceManager, table, jobAssignmentId); try { var jobInput = job.JobInput; S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var copySource = Uri.EscapeDataString(@event.OutputFile.AwsS3Bucket + "/" + @event.OutputFile.AwsS3Key); var s3Bucket = outputLocation.AwsS3Bucket; var s3Key = (!string.IsNullOrWhiteSpace(outputLocation.AwsS3KeyPrefix) ? outputLocation.AwsS3KeyPrefix : string.Empty) + @event.OutputFile.AwsS3Key; try { var destS3 = await outputLocation.GetClientAsync(); await destS3.CopyObjectAsync(new CopyObjectRequest { SourceBucket = @event.OutputFile.AwsS3Bucket, SourceKey = @event.OutputFile.AwsS3Key, DestinationBucket = s3Bucket, DestinationKey = s3Key }); } catch (Exception error) { throw new Exception("Unable to copy output file to bucket '" + s3Bucket + "' with key'" + s3Key + "'", error); } var jobOutput = new JobParameterBag(); jobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = s3Key }; await UpdateJobAssignmentWithOutputAsync(table, jobAssignmentId, jobOutput); await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "COMPLETED"); } catch (Exception error) { Logger.Exception(error); try { await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "FAILED", error.ToString()); } catch (Exception innerError) { Logger.Exception(innerError); } } // Cleanup: Deleting original output file try { var sourceS3 = await @event.OutputFile.GetClientAsync(); await sourceS3.DeleteObjectAsync(new DeleteObjectRequest { BucketName = @event.OutputFile.AwsS3Bucket, Key = @event.OutputFile.AwsS3Key, }); } catch (Exception error) { Logger.Error("Failed to cleanup transcribe output file."); Logger.Exception(error); } }
public static async Task ProcessNotificationAsync(AzureAiServiceWorkerRequest @event) { var jobAssignmentId = @event.JobAssignmentId; var resourceManager = @event.GetAwsV4ResourceManager(); var table = new DynamoDbTable(@event.StageVariables["TableName"]); var azure = new AzureConfig(@event); var azureVideoId = @event.Notification?.Id; var azureState = @event.Notification?.State; if (azureVideoId == null || azureState == null) { Logger.Warn("POST is not coming from Azure Video Indexer. Expected notification to have id and state properties."); return; } try { var authTokenUrl = azure.ApiUrl + "/auth/" + azure.Location + "/Accounts/" + azure.AccountID + "/AccessToken?allowEdit=true"; var customHeaders = new Dictionary <string, string> { ["Ocp-Apim-Subscription-Key"] = azure.SubscriptionKey }; Logger.Debug($"Generate Azure Video Indexer Token: Doing a GET on {authTokenUrl}"); var mcmaHttp = new McmaHttpClient(); var response = await mcmaHttp.GetAsync(authTokenUrl, headers : customHeaders).WithErrorHandling(); var apiToken = await response.Content.ReadAsJsonAsync(); Logger.Debug($"Azure API Token: {apiToken}"); var metadataFromAzureVideoIndexer = $"{azure.ApiUrl}/{azure.Location}/Accounts/{azure.AccountID}/Videos/{azureVideoId}/Index?accessToken={apiToken}&language=English"; Logger.Debug($"Getting Azure video metadata from: {metadataFromAzureVideoIndexer}"); var indexedVideoMetadataResponse = await mcmaHttp.GetAsync(metadataFromAzureVideoIndexer, headers : customHeaders).WithErrorHandling(); var videoMetadata = await indexedVideoMetadataResponse.Content.ReadAsJsonAsync(); Logger.Debug($"Azure AI video metadata: {videoMetadata}"); //Need to hydrate the destination bucket from the job input var workflowJob = await RetrieveJobAsync(resourceManager, table, jobAssignmentId); //Retrieve job inputParameters var jobInput = workflowJob.JobInput; S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } var jobOutputBucket = outputLocation.AwsS3Bucket; var jobOutputKeyPrefix = outputLocation.AwsS3KeyPrefix != null ? outputLocation.AwsS3KeyPrefix : ""; // get the info about the destination bucket to store the result of the job var s3Params = new PutObjectRequest { BucketName = jobOutputBucket, Key = jobOutputKeyPrefix + azureVideoId + "-" + Guid.NewGuid() + ".json", ContentBody = videoMetadata.ToString(), ContentType = "application/json" }; var destS3 = await outputLocation.GetClientAsync(); await destS3.PutObjectAsync(s3Params); //updating JobAssignment with jobOutput var jobOutput = workflowJob.JobOutput ?? new JobParameterBag(); jobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }; await UpdateJobAssignmentWithOutputAsync(table, jobAssignmentId, jobOutput); // Setting job assignment status to COMPLETED await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "COMPLETED"); } catch (Exception error) { Logger.Exception(error); try { await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "FAILED", error.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } }
public async Task <JToken> Handler(JToken @event, ILambdaContext context) { var resourceManager = AwsEnvironment.GetAwsV4ResourceManager(); try { var jobData = new JobBase { Status = "RUNNING", Progress = 63 }; await resourceManager.SendNotificationAsync(jobData, @event["notificationEndpoint"].ToMcmaObject <NotificationEndpoint>()); } catch (Exception error) { Logger.Error("Failed to send notification: {0}", error); } // get transform job id var transformJobId = GetTransformJobId(@event); // in case we did not do a transcode, just return the existing essence ID if (transformJobId == null) { return(@event["data"]["bmEssence"].Value <string>()); } // var transformJob = await resourceManager.ResolveAsync <TransformJob>(transformJobId); S3Locator outputFile; if (!transformJob.JobOutput.TryGet <S3Locator>(nameof(outputFile), false, out outputFile)) { throw new Exception("Unable to get outputFile from AmeJob output."); } var s3Bucket = outputFile.AwsS3Bucket; var s3Key = outputFile.AwsS3Key; var bmc = await resourceManager.ResolveAsync <BMContent>(@event["data"]["bmContent"]?.ToString()); var locator = new S3Locator { AwsS3Bucket = s3Bucket, AwsS3Key = s3Key }; var bme = CreateBmEssence(bmc, locator); bme = await resourceManager.CreateAsync(bme); if (bme?.Id == null) { throw new Exception("Failed to register BMEssence."); } bmc.BmEssences.Add(bme.Id); bmc = await resourceManager.UpdateAsync(bmc); return(bme.Id); }
private BMEssence CreateBmEssence(BMContent bmContent, S3Locator location) => new BMEssence { BmContent = bmContent.Id, Locations = new Locator[] { location } };
internal static async Task ProcessJobAssignmentAsync(TransformServiceWorkerRequest @event) { var resourceManager = @event.Request.GetAwsV4ResourceManager(); var table = new DynamoDbTable(@event.Request.StageVariables["TableName"]); var jobAssignmentId = @event.JobAssignmentId; try { // 1. Setting job assignment status to RUNNING await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "RUNNING", null); // 2. Retrieving WorkflowJob var transformJob = await RetrieveTransformJobAsync(resourceManager, table, jobAssignmentId); // 3. Retrieve JobProfile var jobProfile = await RetrieveJobProfileAsync(resourceManager, transformJob); // 4. Retrieve job inputParameters var jobInput = transformJob.JobInput; // 5. Check if we support jobProfile and if we have required parameters in jobInput ValidateJobProfile(jobProfile, jobInput); switch (jobProfile.Name) { case JOB_PROFILE_CREATE_PROXY_LAMBDA: S3Locator inputFile; if (!jobInput.TryGet <S3Locator>(nameof(inputFile), out inputFile)) { throw new Exception("Invalid or missing input file."); } S3Locator outputLocation; if (!jobInput.TryGet <S3Locator>(nameof(outputLocation), out outputLocation)) { throw new Exception("Invalid or missing output location."); } if (string.IsNullOrWhiteSpace(inputFile.AwsS3Bucket) || string.IsNullOrWhiteSpace(inputFile.AwsS3Key)) { throw new Exception("Not able to obtain input file"); } var data = await inputFile.GetAsync(); var localFileName = "/tmp/" + Guid.NewGuid(); await data.WriteResponseStreamToFileAsync(localFileName, true, CancellationToken.None); var tempFileName = "/tmp/" + Guid.NewGuid() + ".mp4"; var ffmpegParams = new[] { "-y", "-i", localFileName, "-preset", "ultrafast", "-vf", "scale=-1:360", "-c:v", "libx264", "-pix_fmt", "yuv420p", tempFileName }; var ffmpegProcess = await FFmpegProcess.RunAsync(ffmpegParams); File.Delete(localFileName); var s3Params = new PutObjectRequest { BucketName = outputLocation.AwsS3Bucket, Key = (outputLocation.AwsS3KeyPrefix ?? string.Empty) + Guid.NewGuid().ToString() + ".mp4", FilePath = tempFileName, ContentType = "video/mp4" }; var outputS3 = await outputLocation.GetClientAsync(); var putResp = await outputS3.PutObjectAsync(s3Params); var jobOutput = new JobParameterBag(); jobOutput["outputFile"] = new S3Locator { AwsS3Bucket = s3Params.BucketName, AwsS3Key = s3Params.Key }; await UpdateJobAssignmentWithOutputAsync(table, jobAssignmentId, jobOutput); await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "COMPLETED"); break; case JOB_PROFILE_CREATE_PROXY_EC2: var ec2hostname = @event.Request.StageVariables["HostnameInstanceEC2"]; var ec2Url = "http://" + ec2hostname + "/new-transform-job"; var message = new { input = jobInput, notificationEndpoint = new NotificationEndpoint { HttpEndpoint = jobAssignmentId + "/notifications" } }; Logger.Debug("Sending to", ec2Url, "message", message); var mcmaHttp = new McmaHttpClient(); await mcmaHttp.PostAsJsonAsync(ec2Url, message); Logger.Debug("Done"); break; } } catch (Exception ex) { Logger.Exception(ex); try { await UpdateJobAssignmentStatusAsync(resourceManager, table, jobAssignmentId, "FAILED", ex.ToString()); } catch (Exception innerEx) { Logger.Exception(innerEx); } } finally { Logger.Debug("Exiting TransformServiceWorker.ProcessJobAssignmentAsync"); } }