public static async Task Run( [QueueTrigger("%PublishJobsQueueName%", Connection = "MediaStorageAccount")] string assetId, [Queue("%UpdateCMSQueueName%", Connection = "MediaStorageAccount")] ICollector <UpdateReferenceMessage> updateCMSQueue, TraceWriter log) { log.Info($"C# Queue trigger function processed: {assetId}"); var context = MediaContextHelper.CreateContext(); var asset = context.Assets.Where(a => a.Id == assetId).FirstOrDefault(); if (asset == null) { return; } var locator = await context.Locators.CreateAsync( LocatorType.OnDemandOrigin, asset, AccessPermissions.Read, TimeSpan.FromDays(365 * 10)); updateCMSQueue.Add(new UpdateReferenceMessage { AssetId = asset.Id, Status = AssetWorkflowStatus.Published }); }
public static async Task Run( [QueueTrigger("%ContentProtectionJobsQueueName%", Connection = "MediaStorageAccount")] string assetId, [Queue("%PublishJobsQueueName%", Connection = "MediaStorageAccount")] ICollector <string> outputPublishQueue, TraceWriter log) { log.Info($"C# Queue trigger function processed: {assetId}"); var context = MediaContextHelper.CreateContext(); var asset = context.Assets.Where(a => a.Id == assetId).FirstOrDefault(); if (asset == null) { return; } // PlayReady + Widevine await AddPlayReadyWidevineEncryption(context, asset); // FairPlay await AddFairPlayEncryption(context, asset); outputPublishQueue.Add(asset.Id); }
public static async Task Run( [BlobTrigger("%BlobIngestContainer%/{name}", Connection = "MediaStorageAccount")] CloudBlockBlob blob, TraceWriter log) { log.Info($"C# Blob trigger function Processed blob Name: {blob.Name}"); // We're avoiding incomplete blob processing. Eventually the engine // will pick it up from the container, when it's completed. if (blob.CopyState.Status != CopyStatus.Success) { return; } var mediaStorageAccountCredentials = blob.ServiceClient.Credentials; var context = MediaContextHelper.CreateContext(); var asset = await context.Assets.CreateFromBlobAsync(blob, mediaStorageAccountCredentials, AssetCreationOptions.None, CancellationToken.None); // Add the Alternate ID original stored on the Blob Name as partial path asset.AlternateId = blob.Name.Split('-').First(); await asset.UpdateAsync(); var mediaEncoderStandardTaskPreset = Environment.GetEnvironmentVariable("MediaEncoderStandardTaskPreset"); var job = context.Jobs.CreateWithSingleTask( MediaProcessorNames.MediaEncoderStandard, mediaEncoderStandardTaskPreset, asset, $"{asset.Name} MES encoded", AssetCreationOptions.None); var notificationName = Environment.GetEnvironmentVariable("QueueNotificationEndpointName"); var queueName = Environment.GetEnvironmentVariable("EncodeJobsQueueName"); var endpoint = await GetOrCreateQueueNotificationEndpoint(context, notificationName, queueName); job.JobNotificationSubscriptions.AddNew(NotificationJobState.All, endpoint); await job.SubmitAsync(); // Clean up the original Blob from the Storage Account. await blob.DeleteIfExistsAsync(); }
public static async Task Run( [QueueTrigger("%EncodeJobsQueueName%", Connection = "MediaStorageAccount")] dynamic queueItem, [Queue("%ContentProtectionJobsQueueName%", Connection = "MediaStorageAccount")] ICollector <string> outputContentProtectionQueue, TraceWriter log) { log.Info($"C# Queue trigger function processed: {queueItem}"); if (queueItem.Properties.NewState != JobState.Finished.ToString()) { return; } var context = MediaContextHelper.CreateContext(); var jobId = (string)queueItem.Properties.JobId; var job = context.Jobs.Where(j => j.Id == jobId).FirstOrDefault(); if (job == null) { return; } // Copy the Alternate ID from the Mezzanine Asset to the Output Asset var mezzanineAsset = job.InputMediaAssets.FirstOrDefault(); var outputAsset = job.OutputMediaAssets.FirstOrDefault(); outputAsset.AlternateId = mezzanineAsset.AlternateId; await outputAsset.UpdateAsync(); // Cleanup mezzanine asset await mezzanineAsset.DeleteAsync(); // Add Content Protection outputContentProtectionQueue.Add(outputAsset.Id); }
public static async Task Run( [QueueTrigger("%UpdateCMSQueueName%", Connection = "MediaStorageAccount")] UpdateReferenceMessage message, TraceWriter log) { log.Info($"C# Queue trigger function processed: {message.AssetId}"); var context = MediaContextHelper.CreateContext(); var asset = context.Assets.Where(a => a.Id == message.AssetId).FirstOrDefault(); // We're filtering out the Published options, however this would be a great // place to update with error status and implement and error control flow. // e.g. Needs re-encoding and such. if (asset == null || message.Status != AssetWorkflowStatus.Published) { return; } // At this point you have the asset to do call the CMS and update it, all the required // information for the CMS, will be read from the Asset Metadata as follows. var metadata = await asset.GetMetadataAsync(CancellationToken.None); // Sometimes by the time we reach this point the locator ain't ready. Hence we're // we're throwing an exception when metadata isn't ready, to reprocess on a // future iteration. if (metadata == null || metadata.Count() == 0) { throw new Exception($"Asset {asset.Id} metadata is not ready yet."); } var aggregatedMetadata = new { AssetId = asset.Id, AssetAlternateId = asset.AlternateId, BaseStreamingUri = asset.GetSmoothStreamingUri(), Duration = metadata.Max(m => m.Duration), AudioTracksCount = metadata.Max(m => m.AudioTracks.Count()), VideoBitratesCount = metadata.Count(), Bitrate = metadata.SelectMany(m => m.VideoTracks).Max(vt => vt.Bitrate), Height = metadata.SelectMany(m => m.VideoTracks).Max(vt => vt.Height), Width = metadata.SelectMany(m => m.VideoTracks).Max(vt => vt.Width), AspectRatio = metadata.SelectMany(m => m.VideoTracks).Select(vt => $"{vt.DisplayAspectRatioNumerator}:{vt.DisplayAspectRatioDenominator}").FirstOrDefault() }; log.Info($"AssetId: {aggregatedMetadata.AssetId}"); log.Info($"AssetAlternateId: {aggregatedMetadata.AssetAlternateId}"); log.Info($"Base Streaming URL: {asset.GetSmoothStreamingUri()}"); log.Info($"Duration: {aggregatedMetadata.Duration}"); log.Info($"Number of Audio Tracks: {aggregatedMetadata.AudioTracksCount}"); log.Info($"Number of Video Bitrates: {aggregatedMetadata.VideoBitratesCount}"); log.Info($"Bitrate: {aggregatedMetadata.Bitrate}"); log.Info($"Height: {aggregatedMetadata.Height}"); log.Info($"Width: {aggregatedMetadata.Width}"); log.Info($"AspectRatio: {aggregatedMetadata.AspectRatio}"); using (var client = new HttpClient()) { await client.PostAsJsonAsync( Environment.GetEnvironmentVariable("CMSCallbackUrl"), aggregatedMetadata); } }