微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案
微软的云平台媒体服务为流媒体服务提供了多种选择,在使用流媒体服务为企业做流媒体方案时,首先需要确认要流媒体接收目标,如针对广大iOS, Android移动设备,由于它们都支持HLS 格式的流媒体,基于该认知,比较推荐的是使用动态封装,但是必须额外添加流式处理单元,方法是在Azure 门户,点击媒体服务,然后点击所使用的媒体服务,选择流式处理端点标签页,选择需要编辑的流媒体端点,然后再缩放标签页下设置,如下截图所示
使用流式处理单元需要支付一定的费用,且最小单元就是200mbps,根据文档Windows Azure Media Services Pricing Details内容引用如下,我们可以计算出:在使用1个流式处理单元,使用H264 Adaptive Bitrate MP4 Set 720p(范围从 3400 kbps 到 400 kbps)编码方式的情况下,同时可以观看流媒体的客户端个数约为47 个(160/3.4)。
“Question: How do I estimate # of on-demand streaming reserved unit I need?
Answer: For the most conservative estimate you can go directly with SLA limitations – i.e. total bandwidth is 160 Mbps * # of RU’s. To obtain an initial estimate you can take the # of concurrent customers * top bitrate that you are encoding video at. For most video business, I think it is always better to be conservative first and if you end up over provisioning, you could always reduce # of reserved unit.
”
那么如果仅针对少数客户端,可能总数达不到47个,那么出于节省开支的目的,我们还有其他方法吗?答案是肯定的,本文将详细介绍该方案。
针对iOS, Android 客户端,同时观看的客户端数比较少的点播情况下,我们可以使用静态封装方法将编码后的文件包装成HLS即可,这样就可以不使用流式处理单元,需要注意的是,由于Microsoft Azure Media Packager 和 Microsoft Azure Media Encryptor 将在 2015 年 11 月 1 日终结使用,所以本文方案在此日期之后不推荐使用。
在封装为HLS格式之前,必须先将媒体文件转码成smooth streaming 格式,再使用“Windows Azure Media Packager”将其封装为HLS,然后再在iOS 设备和Android 设备上使用自带的浏览器测试HLS 格式的流媒体URL。所以整个流程为:
原始媒体文件——>转码为MP4文件——>将MP4编码为SmoothStreaming 格式——>将SmoothStreaming格式输出封装为HLS格式——>获取静态HLS流媒体URL——>设备测试
需要使用到2个配置文件:
- 1. MP4转为smooth streaming 格式的配置文件:MediaPackager_MP4ToSmooth.xml,内容如下:
<?xml version=”1.0″ encoding=”utf-8″?>
<taskDefinition xmlns=”http://schemas.microsoft.com/iis/media/v4/TM/TaskDefinition#”>
<name>MP4 to Smooth Streams</name>
<id>5e1e1a1c-bba6-11df-8991-0019d1916af0</id>
<description xml:lang=”en” />
<inputFolder />
<properties namespace=”http://schemas.microsoft.com/iis/media/V4/TM/MP4ToSmooth#” prefix=”mp4″>
<property name=”keepSourceNames” value=”false” />
</properties>
<taskCode>
<type>Microsoft.Web.Media.TransformManager.MP4toSmooth.MP4toSmooth_Task, Microsoft.Web.Media.TransformManager.MP4toSmooth, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</type>
</taskCode>
</taskDefinition>
- 2. packager 配置文件MediaPackager_MP4ToSmooth.xml,内容如下:
<?xml version=”1.0″ encoding=”utf-8″ ?>
<taskDefinition xmlns=”http://schemas.microsoft.com/iis/media/v4/TM/TaskDefinition#”>
<name>Smooth Streams to Apple HTTP Live Streams</name>
<id>A72D7A5D-3022-45f2-89B4-1DDC5457C111</id>
<description xml:lang=”en”>Converts on-demand Smooth Streams encoded with H.264 (AVC) video and AAC-LC audio codecs to Apple HTTP Live Streams (MPEG-2 TS) and creates an Apple HTTP Live Streaming playlist (.m3u8) file for the converted presentation.</description>
<inputDirectory></inputDirectory>
<outputFolder>TS_Out</outputFolder>
<properties namespace=”http://schemas.microsoft.com/iis/media/AppleHTTP#” prefix=”hls”>
<property name=”maxbitrate” required=”true” value=”6600000″ helpText=”The maximum bit rate, in bits per second (bps), to be converted to MPEG-2 TS. On-demand Smooth Streams at or below this value are converted to MPEG-2 TS segments. Smooth Streams above this value are not converted. Most Apple devices can play media encoded at bit rates up to 1,600 Kbps.”/>
<property name=”manifest” required=”false” value=”” helpText=”The file name to use for the converted Apple HTTP Live Streaming playlist file (a file with an .m3u8 file name extension). If no value is specified, the following default value is used: <ISM_file_name>-m3u8-aapl.m3u8″/>
<property name=”segment” required=”false” value=”10″ helpText=”The duration of each MPEG-2 TS segment, in seconds. 10 seconds is the Apple-recommended setting for most Apple mobile digital devices.”/>
<property name=”log” required=”false” value=”” helpText=”The file name to use for a log file (with a .log file name extension) that records the conversion activity. If you specify a log file name, the file is stored in the task output folder.” />
<property name=”encrypt” required=”false” value=”false” helpText=”Enables encryption of MPEG-2 TS segments by using the Advanced Encryption Standard (AES) with a 128-bit key (AES-128).” />
<property name=”pid” required=”false” value=”” helpText=”The program ID of the MPEG-2 TS presentation. Different encodings of MPEG-2 TS streams in the same presentation use the same program ID so that clients can easily switch between bit rates.” />
<property name=”codecs” required=”false” value=”false” helpText=”Enables codec format identifiers, as defined by RFC 4281, to be included in the Apple HTTP Live Streaming playlist (.m3u8) file.” />
<property name=”backwardcompatible” required=”false” value=”false” helpText=”Enables playback of the MPEG-2 TS presentation on devices that use the Apple iOS 3.0 mobile operating system.” />
<property name=”allowcaching” required=”false” value=”true” helpText=”Enables the MPEG-2 TS segments to be cached on Apple devices for later playback.” />
<property name=”key” required=”false” value=”” helpText=”The hexadecimal representation of the 16-octet content key value that is used for encryption.” />
<property name=”keyuri” required=”false” value=”” helpText=”An alternate URI to be used by clients for downloading the key file. If no value is specified, it is assumed that the Live Smooth Streaming publishing point provides the key file.” />
<property name=”overwrite” required=”false” value=”true” helpText=”Enables existing files in the output folder to be overwritten if converted output files have identical file names.” />
</properties>
<taskCode>
<type>Microsoft.Web.Media.TransformManager.SmoothToHLS.SmoothToHLSTask, Microsoft.Web.Media.TransformManager.SmoothToHLS, Version=1.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35</type>
</taskCode>
</taskDefinition>
代码可以参考如下:(使用流媒体服务.Net SDK,注意指定媒体服务的账号名字及key)
class Program
{
private static string accName = “[MediaService accountName]”;
private static string accKey = “[MediaService accountKey]”;
private static readonly string _supportFiles =
Path.GetFullPath(@”../..\..\..\..\”);
// Paths to support files (within the above base path). You can use
// the provided sample media files from the “supportFiles” folder, or
// provide paths to your own media files below to run these samples.
private static readonly string singleInputFilePath =
Path.GetFullPath(_supportFiles + @”\azure.wmv”);
private static readonly string outputPath = _supportFiles;
private static CloudMediaContext context;
static void Main(string[] args)
{
context = new CloudMediaContext(accName, accKey);
string inputAssetId = CreateAssetAndUploadFile(context);
//encode to MP4
IJob job = EncodeToMp4(context, inputAssetId);
IAsset mp4Output = job.OutputMediaAssets.FirstOrDefault();
//convert MP4 to smooth streaming
IJob jobSS = ConvertMP4toSmooth(context, mp4Output, mp4Output.Id);
IAsset ssAsset = jobSS.OutputMediaAssets.FirstOrDefault();
string SSStreamingUrlStatic = GetStaticSS(context, ssAsset.Id, LocatorType.OnDemandOrigin);
//package ss to HLS-Static Packaging
IJob jobHLS = SmoothToHLS(context, ssAsset.Id, ssAsset);
var hlsAsset = jobHLS.OutputMediaAssets.FirstOrDefault();
string hlsStreamingUrlStatic = GetStaticHLS(context, hlsAsset.Id, LocatorType.OnDemandOrigin);
string content = “\n Static Packaging Smooth Streaming Url: \n” + SSStreamingUrlStatic +
“\n Static Packaging HLS Url: \n” + hlsStreamingUrlStatic +
“\n Mp4 Url: \n” + mp4StreamingUrl;
Console.WriteLine(“\n Static Packaging Smooth Streaming Url: \n” + SSStreamingUrlStatic);
Console.WriteLine(“\n Static Packaging HLS Url: \n” + hlsStreamingUrlStatic);
Console.WriteLine(“\n Mp4 Url: \n” + mp4StreamingUrl);
Console.WriteLine(“\n Smooth Url: \n” + smoothStreamingUrl);
string outFilePath = Path.GetFullPath(outputPath + @”\” + “StreamingUrl.txt”);
WriteToFile(outFilePath, content);
Console.ReadKey();
Console.ReadKey();
}
private static string CreateAssetAndUploadFile(CloudMediaContext context)
{
var assetName = Path.GetFileNameWithoutExtension(singleInputFilePath);
var inputAsset = context.Assets.Create(assetName, AssetCreationOptions.None);
var assetFile = inputAsset.AssetFiles.Create(Path.GetFileName(singleInputFilePath));
assetFile.UploadProgressChanged += new EventHandler<UploadProgressChangedEventArgs>(assetFile_UploadProgressChanged);
assetFile.Upload(singleInputFilePath);
return inputAsset.Id;
}
static void assetFile_UploadProgressChanged(object sender, UploadProgressChangedEventArgs e)
{
Console.WriteLine(string.Format(“{0} Progress: {1:0} Time: {2}”, ((IAssetFile)sender).Name, e.Progress, DateTime.UtcNow.ToString(@”yyyy_M_d__hh_mm_ss”)));
}
private static IJob EncodeToMp4(CloudMediaContext context, string inputAssetId)
{
var inputAsset = context.Assets.Where(a => a.Id == inputAssetId).FirstOrDefault();
if (inputAsset == null)
throw new ArgumentException(“Could not find assetId: ” + inputAssetId);
var encodingPreset = “H264 Adaptive Bitrate MP4 Set 720p”;
//var encodingPreset = “H264 Broadband 720p”;
IJob job = context.Jobs.Create(“.Net Encoding ” + inputAsset.Name + ” to MP4 job”);
IMediaProcessor latestWameMediaProcessor = (from p in context.MediaProcessors where p.Name == “Windows Azure Media Encoder” select p).ToList()
.OrderBy(wame => new Version(wame.Version)).LastOrDefault();
ITask encodeTask = job.Tasks.AddNew(“.Net Encoding to Mp4 Task “, latestWameMediaProcessor, encodingPreset, TaskOptions.None);
encodeTask.InputAssets.Add(inputAsset);
encodeTask.OutputAssets.AddNew(inputAsset.Name + ” as ” + ” mp4 output asset”, AssetCreationOptions.None);
job.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);
job.Submit();
job.GetExecutionProgressTask(CancellationToken.None).Wait();
return job;
}
private static IJob ConvertMP4toSmooth(CloudMediaContext context, IAsset assetToConvert, string inputAssetId)
{
var inputAsset = context.Assets.Where(a => a.Id == inputAssetId).FirstOrDefault();
if (inputAsset == null)
throw new ArgumentException(“Could not find assetId: ” + inputAssetId);
IJob job = context.Jobs.Create(“Conversion ” + inputAsset.Name + ” to Smooth Streaming job”);
string smoothconfig = Path.GetFullPath(_supportFiles + @”\MediaPackager_MP4ToSmooth.xml”);
string configMp4ToSmooth = File.ReadAllText(Path.GetFullPath(smoothconfig));
IMediaProcessor LatestPackager = (from p in context.MediaProcessors where p.Name == “Windows Azure Media Packager” select p).ToList()
.OrderBy(wame => new Version(wame.Version)).LastOrDefault();
ITask convertTask = job.Tasks.AddNew(“.Net Conversion MP4 to Smooth Streaming Task for ” + inputAsset.Name,
LatestPackager, configMp4ToSmooth, TaskOptions.None);
convertTask.InputAssets.Add(assetToConvert);
convertTask.OutputAssets.AddNew(“.Net output-MP4 converted Smooth Streaming-” + inputAsset.Name, AssetCreationOptions.None);
job.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);
job.Submit();
job.GetExecutionProgressTask(CancellationToken.None).Wait();
return job;
}
private static IJob SmoothToHLS(CloudMediaContext context, string inputAssetId, IAsset asset)
{
var inputAsset = context.Assets.Where(a => a.Id == inputAssetId).FirstOrDefault();
if (inputAsset == null)
throw new ArgumentException(“Could not find assetId: ” + inputAssetId);
var conversionPreset = “HLS”;
IJob job = context.Jobs.Create(“Conversion ” + inputAsset.Name + ” to ” + conversionPreset);
//var ssOutput = encodeTask.OutputAssets.AddNew(“output-Silverlight-” + inputAsset.Name,
// AssetCreationOptions.None);
string smooth2HLSFilePath = Path.GetFullPath(_supportFiles + @”\MediaPackager_SmoothToHLS.xml”);
var packager = context.MediaProcessors.Where(m => m.Name == “Windows Azure Media Packager”).First();
var conversionTask = job.Tasks.AddNew(“Conversion Task for ” + inputAsset.Name, packager,
File.ReadAllText(smooth2HLSFilePath), TaskOptions.None);
conversionTask.InputAssets.Add(asset);
conversionTask.OutputAssets.AddNew(
“output-HLS-” + inputAsset.Name, AssetCreationOptions.None);
job.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);
job.Submit();
job.GetExecutionProgressTask(CancellationToken.None).Wait();
return job;
}
static void JobStateChanged(object sender, JobStateChangedEventArgs e)
{
Console.WriteLine(string.Format(“{0}\n State: {1}\n Time: {2}\n\n”,
((IJob)sender).Name, e.CurrentState, DateTime.UtcNow.ToString(@”yyyy_M_d__hh_mm_ss”)));
}
static void WriteToFile(string outFilePath, string fileContent)
{
StreamWriter sr = File.CreateText(outFilePath);
sr.Write(fileContent);
sr.Close();
}
private static string GetStaticSS(CloudMediaContext context, string outputAssetId, LocatorType type)
{
var daysForWhichStreamingUrlIsActive = 365;
var outputAsset = context.Assets.Where(a => a.Id == outputAssetId).FirstOrDefault();
var accessPolicy = context.AccessPolicies.Create(outputAsset.Name,
TimeSpan.FromDays(daysForWhichStreamingUrlIsActive),
AccessPermissions.Read | AccessPermissions.List);
var assetFiles = outputAsset.AssetFiles.ToList();
if (type == LocatorType.OnDemandOrigin)
{
var assetFile = assetFiles.Where(f => f.Name.ToLower().EndsWith(“.ism”)).FirstOrDefault();
if (assetFile != null)
{
var locator = context.Locators.CreateLocator(LocatorType.OnDemandOrigin, outputAsset, accessPolicy);
Uri smoothUri = new Uri(locator.Path + assetFile.Name + “/manifest”);
return smoothUri.ToString();
}
}
if (type == LocatorType.Sas)
{
var mp4Files = assetFiles.Where(f => f.Name.ToLower().EndsWith(“.mp4″)).ToList();
var assetFile = mp4Files.OrderBy(f => f.ContentFileSize).LastOrDefault(); //Get Largest File
if (assetFile != null)
{
var locator = context.Locators.CreateLocator(LocatorType.Sas, outputAsset, accessPolicy);
var mp4Uri = new UriBuilder(locator.Path);
mp4Uri.Path += “/” + assetFile.Name;
return mp4Uri.ToString();
}
}
return string.Empty;
}
private static string GetStaticHLS(CloudMediaContext context, string outputAssetId, LocatorType type)
{
var daysForWhichStreamingUrlIsActive = 365;
var outputAsset = context.Assets.Where(a => a.Id == outputAssetId).FirstOrDefault();
var accessPolicy = context.AccessPolicies.Create(outputAsset.Name,
TimeSpan.FromDays(daysForWhichStreamingUrlIsActive),
AccessPermissions.Read | AccessPermissions.List);
var assetFiles = outputAsset.AssetFiles.ToList();
foreach (var f in assetFiles.Where(x => x.Name.EndsWith(“.ism”)))
{
if (f.Name.Contains(“m3u8″))
{
#region publish HLS to a WAMS origin
Console.WriteLine(“will create a new locator for HLS”);
var originLocator = context.Locators.CreateLocator(LocatorType.OnDemandOrigin, outputAsset, accessPolicy);
string urlForClientStreaming = originLocator.Path + f.Name
+ “/manifest(format=m3u8-aapl)”;
return urlForClientStreaming.ToString();
#endregion
}
}
return string.Empty;
}
}
最终输出如:http://test20140404.origin.mediaservices.windows.net/1dcaef59-56b1-4bf9-9dd2-8502d62d5095/azure-m3u8-aapl.ism/manifest(format=m3u8-aapl)
微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案的更多相关文章
- 微软云平台媒体服务实践系列 2- 使用动态封装为iOS, Android , Windows 等多平台提供视频点播(VoD)方案
文章微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案 介绍了如何针对少数iOS, Android 客户端的场景,出于节约成本的目的使用媒体服务的静 ...
- 微软云平台windows azure入门系列八课程
微软云平台windows azure入门系列八课程: Windows Azure入门教学系列 (一): 创建第一个WebRole程序与部署 Windows Azure入门教学系列 (二): 创建第一个 ...
- Microsoft Azure 微软云平台系列新品发布
在移动为先,云为先的今天,微软为拥抱云文化的企业提供了技术和工具.利用创新且全面的移动解决方案和开发者工具,微软有独到之处,它帮助所有客户在云为先时代中发现潜在价值. 正如希望加快云创新步伐的你们所期 ...
- 云平台发展前沿报告 微软云平台——Windows Azure
微软云平台——Windows Azure Windows Azure 是微软研发的公有云计算平台.该平台可供企业在互联网上运行应用,并可进行扩展.通过Windows Azure,企业能够在多个数据中心 ...
- 【转帖】云平台发现服务构建:为什么不使用ZooKeeper
http://www.chinacloud.cn/show.aspx?id=19979&cid=16 [日期:2015-04-29] 来源:dockerone 作者: [字体:大 中 小] ...
- DevOps云翼日志服务实践
10月30日,全球权威数据调研机构IDC正式发布<IDCMarketScape:中国DevOps云市场2019,厂商评估>报告.京东云凭借丰富的场景和实践能力,以及高质量的服务交付和平台稳 ...
- .Net 分布式云平台基础服务建设说明概要
1) 背景 建设云平台的基础框架,用于支持各类云服务的业务的构建及发展. 2) 基础服务 根据目前对业务的理解和发展方向,总结抽象出以下几个基础服务,如图所示 3) 概要说明 基础服务的发展会根 ...
- 【涂鸦物联网足迹】涂鸦云平台消息服务—顺带Pulsar简单介绍
前序系列文章>>> [涂鸦物联网足迹]涂鸦云平台标准指令集 开放消息平台主要通过 Pulsar 主动推送各种事件数据给外部合作伙伴,以满足合作伙伴对消息实时性和消息持久化的要求. 一 ...
- Windows Azure HandBook (10) 测试本地网络到微软云的延迟
<Windows Azure Platform 系列文章目录> 之前遇到一些微软云的客户,在使用海外数据中心的时候,需要评估本地网络到微软云网络的延迟. 我们建议部署到微软云上的服务,离最 ...
随机推荐
- 怎样用VB编写.DLL动态链接库文件
VB一般可以生成两种特殊的DLL,一个是ActiveX DLL和ActiveX Control(*.ocx).这两种DLL都是VB支持的标准类型,在VB自身的例子中有,你可以参考.更详细的介绍可以参考 ...
- Tomcat安装后启动一闪而过
出现这种问题一般是环境变量没配置好.除了JDK环境变量还有Tomcat环境变量:CATALINA_HOME 和CATALINA_BASE 虽然JDK里面会含有JRE,但是最好是在环境变量里面也配置一个 ...
- Android控件大全(三)——RecyclerView
是时候用RecyclerView来替换ListView和GridView了 好处就不多说了,百度一搜一大把,来介绍下用法 先定义个适配器: public class BottomSheetAdapte ...
- JQ仿select框
点击[cy_title]后弹出[cy_list]层,选中里面的元素把值赋给 [cy_title] 在[cy_list] 打开的时候,点击其他地方可以关闭: HTML: <div class=&q ...
- VBS创建数据表
'创建数据表'参数:strDBPath 字符串型 数据库路径'参数:strTableName 字符串型 需要创建的数据表的名称'参数:strColumnName 字符串型 初始化的字段名称,其实可以算 ...
- PHP:PHP页面编码问题(转载)
MySQL数据库编码.html页面编码.PHP或html文件本身编码要全部一致. 1.MySQL数据库编码:建立数据库时指定编码(如gbk_chinese_ci),建立数据表.建立字段.插入数据时不要 ...
- PHP超时处理全面总结(转)
[ 概述 ] 在PHP开发中工作里非常多使用到超时处理到超时的场合,我说几个场景: 1. 异步获取数据如果某个后端数据源获取不成功则跳过,不影响整个页面展现 2. 为了保证Web服务器不会因为当个页面 ...
- C++利用注册表添加桌面右键新建菜单
对于程序员来说,新建一个cpp文件是再频繁不过的事情了. 为了方便,我们习惯在桌面右键新建文件,而不是新建一个文本文档,然后修改后缀名. 百度谷歌查询了一下,终于知道如何添加注册表. 手痒,抽出时间用 ...
- basis基本tcode
SM21 ST11 SM50 查看work process 使用情况 操作相关的查询功能 SM## 常用tcode SM01 锁定事务 SM04 用户清单 SM05 HTTP ...
- CentOS学习笔记--Tomcat安装
Tomcat安装 通常情况下我们要配置Tomcat是很容易的一件事情,但是如果您要架设多用户多服务的Java虚拟主机就不那么容易了.其中最大的一个问题就是Tomcat执行权限.普通方式配置的Tomca ...