Azure媒体服务无法获取PublishAssetURL,.ism清单文件返回Null
嘿,我来帮你捋清楚这个问题:你把MP4上传到Azure Blob后,用CreateAssetFromExistingBlobs方法创建了资产,但代码里找.ism结尾的文件时一直返回Null,导致拿不到发布URL对吧?这事儿大概率是因为你没触发Media Services的编码打包流程,毕竟原始MP4本身就不带.ism清单文件呀!
下面给你一步步拆解排查和解决方法:
1. 先确认资产里到底有啥文件
首先你得搞清楚,你上传的只是原始MP4,Azure Media Services不会平白无故给你生成.ism文件——只有当你把MP4编码成自适应比特率的平滑流格式时,才会自动生成.ism、.ismv这些文件。
你可以先加段代码看看资产里的文件列表:
// 创建完资产后,遍历所有AssetFiles打个日志 foreach(var file in asset.AssetFiles) { Console.WriteLine($"当前资产里的文件:{file.Name}"); }
如果输出里只有你上传的MP4,那说明确实没有.ism文件,这是正常情况,不是bug,是流程没做全~
2. 触发编码作业生成平滑流文件
要是你需要的是可用于流媒体播放的发布URL,那必须先把原始MP4编码成平滑流格式,这样才会生成.ism清单。具体怎么做呢?
第一步:创建编码作业
在创建完原始资产后,新增一个编码作业,用Media Encoder Standard编码器,选Adaptive Streaming预设就行:
// 先拿到你的Media Services上下文(假设你已经有context实例了) var encoder = context.MediaProcessors.GetLatestMediaProcessorByName("Media Encoder Standard"); // 创建一个编码作业 var job = context.Jobs.Create("把MP4转成自适应流"); // 添加编码任务,指定用Adaptive Streaming预设 var task = job.Tasks.AddNew("编码任务", encoder, "Adaptive Streaming", TaskOptions.None); // 把之前创建的原始资产作为输入 task.InputAssets.Add(asset); // 创建输出资产,用来存编码后的文件 var outputAsset = context.Assets.Create("编码后的输出资产", AssetCreationOptions.None); task.OutputAssets.Add(outputAsset); // 提交作业 job.Submit(); // 等待作业完成(生产环境别这么阻塞,建议用异步监听事件哈) job.GetExecutionProgressTask(CancellationToken.None).Wait();
第二步:从输出资产里拿.ism文件
等编码作业跑完,输出资产里就有.ism清单文件了,这时候再查找就不会返回Null了:
// 从编码后的输出资产里找.ism文件 var manifestFile = outputAsset.AssetFiles.Where(x => x.Name.EndsWith(".ism")).FirstOrDefault(); if(manifestFile != null) { // 发布资产并获取流媒体URL var accessPolicy = context.AccessPolicies.Create("流媒体访问策略", TimeSpan.FromDays(30), AccessPermissions.Read); var locator = context.Locators.CreateLocator(LocatorType.OnDemandOrigin, outputAsset, accessPolicy); var publishUrl = locator.Path + manifestFile.Name + "/manifest"; Console.WriteLine("拿到发布URL啦:" + publishUrl); }
3. 要是不想编码,直接发布原始MP4就行
如果你只是想直接分享原始MP4的URL,完全不用找.ism文件,直接用MP4文件生成发布链接就好:
var mp4File = asset.AssetFiles.Where(x => x.Name.EndsWith(".mp4")).FirstOrDefault(); if(mp4File != null) { var accessPolicy = context.AccessPolicies.Create("直接访问MP4策略", TimeSpan.FromDays(30), AccessPermissions.Read); var locator = context.Locators.CreateLocator(LocatorType.OnDemandOrigin, asset, accessPolicy); var publishUrl = locator.Path + mp4File.Name; Console.WriteLine("原始MP4的直接访问URL:" + publishUrl); }
4. 检查你的CreateAssetFromExistingBlobs方法
另外,你得确认这个方法是不是正确把Blob关联到资产里了,有没有不小心过滤掉文件?比如方法里是不是只处理了特定前缀的Blob,或者复制Blob的时候出了问题?
给你一个靠谱的核心实现参考:
public string CreateAssetFromExistingBlobs(CloudBlobContainer sourceBlobContainer, string assetName) { var context = GetMediaServicesContext(); // 这里替换成你获取Media Services上下文的方法 var asset = context.Assets.Create(assetName, AssetCreationOptions.None); // 遍历容器里所有Blob var blobs = sourceBlobContainer.ListBlobs(null, true, BlobListingDetails.None); foreach(var blobItem in blobs) { var blob = blobItem as CloudBlockBlob; if(blob == null) continue; // 创建AssetFile并关联到资产 var assetFile = asset.AssetFiles.Create(blob.Name); assetFile.ContentFileSize = blob.Properties.Length; assetFile.Update(); // 生成临时SAS权限,复制Blob到资产对应的存储容器 var tempAccessPolicy = context.AccessPolicies.Create("临时复制权限", TimeSpan.FromHours(4), AccessPermissions.Write); var locator = context.Locators.CreateLocator(LocatorType.Sas, asset, tempAccessPolicy); var destinationBlob = sourceBlobContainer.GetBlockBlobReference(blob.Name); destinationBlob.StartCopy(blob.Uri); // 等待复制完成(生产环境建议用异步监听,别阻塞线程) while(destinationBlob.CopyState.Status == CopyStatus.Pending) { Thread.Sleep(1000); destinationBlob.FetchAttributes(); } } asset.Update(); return asset.Id; }
确保这个方法确实把所有需要的Blob都关联到资产里了,没漏掉任何文件~
内容的提问来源于stack exchange,提问作者Osama Aftab

