private static async Task StoreBreakdownJsonInCosmos(VideoBreakdown videoBreakdownJson, TraceWriter log) { //string CosmosCollectionName = ConfigurationManager.AppSettings["CosmosCollectionName"]; //if (String.IsNullOrEmpty(CosmosCollectionName)) // throw new ApplicationException("CosmosCollectionName app setting not set"); var cosmosHelper = new CosmosHelper(log); var collectionName = "Breakdowns"; var client = cosmosHelper.GetCosmosClient(collectionName); var json = JsonConvert.SerializeObject(videoBreakdownJson); cosmosHelper.LogMessage($"saving json: {json}"); // save the json as a new document try { Document r = await client.UpsertDocumentAsync( UriFactory.CreateDocumentCollectionUri(CosmosHelper.CosmosDatabasename, collectionName), videoBreakdownJson); } catch (Exception e) { cosmosHelper.LogMessage($"error inserting document in cosmos: {e.Message}"); // ignore for now, but maybe should replace the document if it already exists.. // seems to be caused by dev environment where queue items are being reprocesssed } }
public static async Task RunAsync( [QueueTrigger("%EncodingCompleteQueue%", Connection = "AzureWebJobsStorage")] string myQueueItem, TraceWriter log) { var videoIndexerHelper = new VideoIndexerHelper(log); var cosmosHelper = new CosmosHelper(log); var msg = JsonConvert.DeserializeObject <NotificationMessage>(myQueueItem); if (msg.EventType != NotificationEventType.TaskStateChange) { return; // ignore anything but job complete } var newJobStateStr = msg.Properties.FirstOrDefault(j => j.Key == Constants.NEWSTATE).Value; if (newJobStateStr == Enums.StateEnum.Finished.ToString()) { var jobId = msg.Properties["JobId"]; var taskId = msg.Properties["TaskId"]; _context = MediaServicesHelper.Context; var job = _context.Jobs.Where(j => j.Id == jobId).FirstOrDefault(); if (job == null) { videoIndexerHelper.LogMessage($"Job for JobId:{jobId} is null"); return; } var task = job.Tasks.Where(l => l.Id == taskId).FirstOrDefault(); if (task == null) { videoIndexerHelper.LogMessage($"Task for taskId:{taskId} is null"); return; } var outputAsset = task.OutputAssets[0]; var inputAsset = task.InputAssets[0]; cosmosHelper.LogMessage("Read policy"); var readPolicy = _context.AccessPolicies.Create("readPolicy", TimeSpan.FromHours(4), AccessPermissions.Read); var outputLocator = _context.Locators.CreateLocator(LocatorType.Sas, outputAsset, readPolicy); cosmosHelper.LogMessage("Create cloud blob client"); var destBlobStorage = BlobHelper.AmsStorageAccount.CreateCloudBlobClient(); cosmosHelper.LogMessage("get asset container"); // Get the asset container reference var outContainerName = new Uri(outputLocator.Path).Segments[1]; var outContainer = destBlobStorage.GetContainerReference(outContainerName); cosmosHelper.LogMessage("use largest single mp4 "); // use largest single mp4 output (highest bitrate) to send to Video Indexer var biggestblob = outContainer.ListBlobs().OfType <CloudBlockBlob>() .Where(b => b.Name.ToLower().EndsWith(".mp4")) .OrderBy(u => u.Properties.Length).Last(); cosmosHelper.LogMessage("GetSasUrl"); var sas = videoIndexerHelper.GetSasUrl(biggestblob); cosmosHelper.LogMessage(" submit to VI "); // Submit processing job to Video Indexer await videoIndexerHelper.SubmitToVideoIndexerAsync(biggestblob.Name, sas, inputAsset.AlternateId, log); } }
public static async Task Run([QueueTrigger("%InputQueue%", Connection = "AzureWebJobsStorage")] VippyProcessingState manifest, [Blob("%AmsBlobInputContainer%/{BlobName}", FileAccess.ReadWrite)] CloudBlockBlob videoBlobTriggered, [Blob("%ExistingAmsBlobInputContainer%/{BlobName}", FileAccess.ReadWrite)] CloudBlockBlob videoBlobExisting, TraceWriter log) { //================================================================================ // Function AMSInputQueueHandler // Purpose: // This is where the start of the pipeline work begins. It will submit an encoding // job to Azure Media Services. When that job completes asyncronously, a notification // webhook will be called by AMS which causes the next stage of the pipeline to // continue. //================================================================================ CloudBlockBlob videoBlob = null; if (manifest.Origin == Enums.OriginEnum.Existing) { videoBlob = videoBlobExisting; } else if (manifest.Origin == Enums.OriginEnum.Trigger) { videoBlob = videoBlobTriggered; } if (videoBlob == null) { log.Error("There is being an error, videoblog not initialized, not marked as existing or trigger or video is null"); return; } var context = MediaServicesHelper.Context; var cosmosHelper = new CosmosHelper(log); var toDeleteContainerName = Environment.GetEnvironmentVariable("AmsBlobToDeleteContainer"); // only set the starttime if it wasn't already set in blob watcher function (that way // it works if the job is iniaited by using this queue directly if (manifest.StartTime == null) { manifest.StartTime = DateTime.Now; } var videofileName = videoBlob.Name; // get a new asset from the blob, and use the file name if video title attribute wasn't passed. IAsset newAsset; try { newAsset = BlobHelper.CreateAssetFromBlob(videoBlob, videofileName, log) .GetAwaiter().GetResult(); } catch (Exception e) { throw new ApplicationException($"Error occured creating asset from Blob;/r/n{e.Message}"); } // If an internal_id was passed in the metadata, use it within AMS (AlternateId) and Cosmos(Id - main document id) for correlation. // if not, generate a unique id. If the same id is ever reprocessed, all stored metadata // will be overwritten. newAsset.AlternateId = manifest.AlternateId; newAsset.Update(); manifest.AmsAssetId = newAsset.Id; log.Info($"Deleting the file {videoBlob.Name} from the container "); //move the video to the to delete folder await BlobHelper.Move(videoBlob.Container.Name, toDeleteContainerName, videofileName, log); //move the manifest to the to delete folder string manifestName = videofileName.Remove(videofileName.IndexOf('.')) + ".json"; log.Info($"Deleting the file {manifestName} from the container "); await BlobHelper.Move(videoBlob.Container.Name, toDeleteContainerName, manifestName, log); // copy blob into new asset // create the encoding job var job = context.Jobs.Create("MES encode from input container - ABR streaming"); // Get a media processor reference, and pass to it the name of the // processor to use for the specific task. var processor = MediaServicesHelper.GetLatestMediaProcessorByName("Media Encoder Standard"); var task = job.Tasks.AddNew("encoding task", processor, "Content Adaptive Multiple Bitrate MP4", TaskOptions.None ); task.Priority = 100; task.InputAssets.Add(newAsset); // setup webhook notification var keyBytes = new byte[32]; // Check for existing Notification Endpoint with the name "FunctionWebHook" var existingEndpoint = context.NotificationEndPoints.Where(e => e.Name == "FunctionWebHook").FirstOrDefault(); INotificationEndPoint endpoint; //if (existingEndpoint != null) //{ // endpoint = existingEndpoint; //} //else try { endpoint = context.NotificationEndPoints.Create("FunctionWebHook", NotificationEndPointType.WebHook, WebHookEndpoint, keyBytes); } catch (Exception) { throw new ApplicationException( $"The endpoing address specified - '{WebHookEndpoint}' is not valid."); } task.TaskNotificationSubscriptions.AddNew(NotificationJobState.FinalStatesOnly, endpoint, false); cosmosHelper.LogMessage($"Add an output asset to contain the results of the job"); // Add an output asset to contain the results of the job. // This output is specified as AssetCreationOptions.None, which // means the output asset is not encrypted. task.OutputAssets.AddNew(videofileName, AssetCreationOptions.None); // Starts the job in AMS. AMS will notify the webhook when it completes job.Submit(); cosmosHelper.LogMessage($"Saving on cosmos DB"); // update processing progress with id and metadata payload await cosmosHelper.StoreProcessingStateRecordInCosmosAsync(manifest); cosmosHelper.LogMessage($"AMS encoding job submitted for {videofileName}"); }