文章微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案  介绍了如何针对少数iOS, Android 客户端的场景,出于节约成本的目的使用媒体服务的静态封装实现视频点播方案。实际应用中,所支持的客户端越多,那么所获得的用户越多,客户端多种多样:PC端, Xbox, iOS, Android, Windows 等移动设备端,针对如此之大的客户端市场,能否搭建一套全面的点播方案呢?答案是肯定的,本文将基于微软云平台媒体服务介绍如何实现跨多移动客户端的视频点播方案。

微软的云平台媒体服务为流媒体服务提供了多种选择,在使用流媒体服务为企业做流媒体方案时,首先需要确认要流媒体接收目标,如针对广大iOS, Android移动设备,由于它们都支持HLS 格式的流媒体,微软平台支持smooth streaming 格式,基于该认知,比较推荐的是使用动态封装,但是必须额外添加流式处理单元,方法是在Azure 门户,点击媒体服务,然后点击所使用的媒体服务,选择流式处理端点标签页,选择需要编辑的流媒体端点,然后再缩放标签页下设置,如下截图所示:

使用动态封装制定支持Windows, iOS, Android 平台的视频点播方案的流程如下截图所示:原始媒体文件——>转码为MP4文件——>使用动态封装功能,按照客户端需求提供流媒体码流, 构造流媒体地址——>测试。

此外我们还可以在编码过程中为视频创建缩略图, 需要使用到相应的配置文件Thumbnail_Configuration.xml,内容如下:

<?xml version=”1.0″ encoding=”utf-8″?>

<Thumbnail Size=”100%,*” Type=”Jpeg” Filename=”{OriginalFilename}_{Size}_{ThumbnailTime}_{ThumbnailIndex}_{Date}_{Time}.{DefaultExtension}”>

<Time Value=”10%” Step=”10%” Stop=”95%”/>

</Thumbnail>

整个代码流程可以参考如下:(使用流媒体服务.Net SDK,注意指定媒体服务的账号名字(accName)及key(accKey))

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).

// provide paths to your own media files below to run.

private static readonly string singleInputFilePath =

Path.GetFullPath(_supportFiles + @”\azure.wmv”);

private static readonly string outputPath = _supportFiles;

private static CloudMediaContext context;

private static readonly string configFilePath = Path.GetFullPath(_supportFiles + @”\Thumbnail_Configuration.xml”);

static void Main(string[] args)

{

context = new CloudMediaContext(accName, accKey);

string inputAssetId = CreateAssetAndUploadFile(context);

//thumbnail

IJob jobThumb = ThumbnailMaker(context, inputAssetId);

string thumbnailUri = GetThumbnailUrl(context, jobThumb.OutputMediaAssets[0]);

//encode to MP4

IJob job = EncodeToMp4(context, inputAssetId);

IAsset mp4Output = job.OutputMediaAssets.FirstOrDefault();

//Dynamic Packaging

var mp4Asset = job.OutputMediaAssets.FirstOrDefault();

string mp4Url = GetDynamicStreamingUrl(context, mp4Asset.Id, LocatorType.Sas);

string smoothStreamingUrl = GetDynamicStreamingUrl(context, mp4Asset.Id, LocatorType.OnDemandOrigin);

string hlsStreamingUrl = smoothStreamingUrl + “(format=m3u8-aapl)”;

string mpegdashStreamingUrl = smoothStreamingUrl + “(format=mpd-time-csf)”;

//Output

string content = “\n Thumbnail Url:    \n” + thumbnailUri

+”\n Mp4 Url:    \n” + mp4Url

+ “\n Dynamic Packaging Smooth Url: \n” + smoothStreamingUrl

+”\n Dynamic Packaging HLS Url:    \n” + hlsStreamingUrl

+”\n Dynamic Packaging MPEG DASH Url:    \n” + mpegdashStreamingUrl;

Console.WriteLine(“\n Thumbnail Url:    \n” + thumbnailUri);

Console.WriteLine(“\n Mp4 Url:    \n” + mp4Url);

Console.WriteLine(“\n Smooth Url: \n” + smoothStreamingUrl);

Console.WriteLine(“\n HLS Url:    \n” + hlsStreamingUrl);

Console.WriteLine(“\n MPEG DASH Url:    \n” + mpegdashStreamingUrl);

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”;

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;

}

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 GetDynamicStreamingUrl(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 IJob ThumbnailMaker(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);

IMediaProcessor latestWameMediaProcessor = (from p in context.MediaProcessors where p.Name == “Windows Azure Media Encoder” select p).ToList()

.OrderBy(wame => new Version(wame.Version)).LastOrDefault();

//thumbnail

//Get thumbnail configuration from a XML file

var ThumbnailConfig = File.ReadAllText(configFilePath);

// Create a job

IJob jobThumbnail = context.Jobs.Create(“Thumbnail job Thumbnail Making ” + inputAsset.Name);

//Create a task

ITask thumbnailTask = jobThumbnail.Tasks.AddNew(“Thumbnail task”,

latestWameMediaProcessor,

ThumbnailConfig,

TaskOptions.None);

thumbnailTask.InputAssets.Add(inputAsset);

thumbnailTask.OutputAssets.AddNew(inputAsset.Name + ” as Thumbnail job”, AssetCreationOptions.None);

jobThumbnail.StateChanged += new EventHandler<JobStateChangedEventArgs>(JobStateChanged);

jobThumbnail.Submit();

jobThumbnail.GetExecutionProgressTask(CancellationToken.None).Wait();

return jobThumbnail;

}

private static string GetThumbnailUrl(CloudMediaContext context, IAsset outputAsset)

{

var output = outputAsset;

var accessPolicy = context.AccessPolicies.Create(“Read Policy”, TimeSpan.FromDays(10), AccessPermissions.Read);

var locator = context.Locators.CreateSasLocator(output, accessPolicy);

var primaryFile = (from a in output.AssetFiles

where a.IsPrimary == true

select a).FirstOrDefault();

return locator.BaseUri + ‘/’ + primaryFile.Name + locator.ContentAccessComponent;

}

}

最终我们可以获得编码后的视频缩略图地址、MP4 文件地址(可以直接用于网页,使用Video tag)、Smooth Streaming, HLS, MPEG-DASH地址:

Thumbnail URL: https://myappsstorage.blob.core.windows.net/asset-e157385d-1500-80c2-af19-f1e4cbb3271f/azure_1280x720_00.00.00.6346000_1_3-16-2015_8.06%20AM.jpg?sv=2012-02-12&sr=c&si=804d04b5-3450-4d00-9ed8-8d16913c2330&sig=n7chFUFuHRA3R9RMpknxvFBkCbsG2kDgx0VWd6AJfiE%3D&se=2015-03-26T08%3A06%3A18Z

MP4 URL: https://myappsstorage.blob.core.windows.net:443/asset-e157385d-1500-80c2-9cfd-f1e4cbb3517b/azure_H264_3400kbps_AAC_und_ch2_96kbps.mp4?sv=2012-02-12&sr=c&si=150c9ab8-a78c-4557-aab8-1deba001a998&sig=pcl58cJgJ8lxRPSic4tM0us5wMSON8MIEUPDZBsXa3Y%3D&se=2016-03-15T08%3A07%3A30Z

Smooth Streaming URL:   http://test20140404.origin.mediaservices.windows.net/e5b3f1e4-a662-41da-b8af-4423aaea492b/azure.ism/manifest

HLS:http://test20140404.origin.mediaservices.windows.net/e5b3f1e4-a662-41da-b8af-4423aaea492b/azure.ism/manifest(format=m3u8-aapl)

MPEG-DASH: http://test20140404.origin.mediaservices.windows.net/e5b3f1e4-a662-41da-b8af-4423aaea492b/azure.ism/manifest(format=mpd-time-csf)

自适应流媒体的最大的特征是根据客户端的网络带宽情况自适应地选择对应码流媒体内容进行传输,保证流畅的视频播放体验,因此需要特殊的客户端播放器。对于这3种常用的流媒体格式,可以使用如下播放器进行测试:

Smooth Streaming player: http://smf.cloudapp.net/healthmonitor

HLS: JW player: http://www.jwplayer.com/partners/azure/

MPEG-DASH player: http://dashplayer.azurewebsites.net/

微软官方DEMO测试地址:http://ampdemo.azureedge.net/

https://blogs.msdn.microsoft.com/lj/2015/02/09/1-ios-android-23454/

微软云平台媒体服务实践系列 2- 使用动态封装为iOS, Android , Windows 等多平台提供视频点播(VoD)方案的更多相关文章

  1. 微软云平台媒体服务实践系列 1- 使用静态封装为iOS, Android 设备实现点播(VoD)方案

    微软的云平台媒体服务为流媒体服务提供了多种选择,在使用流媒体服务为企业做流媒体方案时,首先需要确认要流媒体接收目标,如针对广大iOS, Android移动设备,由于它们都支持HLS 格式的流媒体,基于 ...

  2. 使用 Jenkins 搭建 iOS/Android 持续集成打包平台【转】

    背景描述 根据项目需求,现要在团队内部搭建一个统一的打包平台,实现对iOS和Android项目的打包.而且为了方便团队内部的测试包分发,希望在打包完成后能生成一个二维码,体验用户(产品.运营.测试等人 ...

  3. DevOps云翼日志服务实践

    10月30日,全球权威数据调研机构IDC正式发布<IDCMarketScape:中国DevOps云市场2019,厂商评估>报告.京东云凭借丰富的场景和实践能力,以及高质量的服务交付和平台稳 ...

  4. Gartner 认可 Microsoft 为应用程序平台即服务的领导者

    对于 Windows Azure 而言,2013 年是了不起的一年.客户使用量每月都创新高:4 月份 Windows Azure 基础结构服务一经正式发布即受到前所未有的青睐,成为重要的里程碑.Gar ...

  5. Windows Azure HandBook (10) 测试本地网络到微软云的延迟

    <Windows Azure Platform 系列文章目录> 之前遇到一些微软云的客户,在使用海外数据中心的时候,需要评估本地网络到微软云网络的延迟. 我们建议部署到微软云上的服务,离最 ...

  6. .NET Core on K8S学习实践系列文章索引(Draft版)

    一.关于这个系列 自从去年(2018年)底离开工作了3年的M公司加入X公司之后,开始了ASP.NET Core的实践,包括微服务架构与容器化等等.我们的实践是渐进的,当我们的微服务数量到了一定值时,发 ...

  7. Golang在视频直播平台的高性能实践

    http://toutiao.com/i6256894054273909249/ 熊猫 TV 是一家视频直播平台,先介绍下我们系统运行的环境,下面这 6 大服务只是我们几十个服务中的一部分,由于并发量 ...

  8. Azure 媒体服务的 RTMP 支持和实时编码器

    Cenk Dingiloglu Azure 媒体服务高级项目经理 直播流媒体目前已在公共预览版中提供,其中一项受支持的输入协议便是 RTMP.RTMP 是用于输入和分发包括直播流媒体在内的丰富媒体的一 ...

  9. Android之微信开放平台实现分享(分享好友和朋友圈)

    开发中分享操作往往经常遇到,而且还是一些比较大型一定的平台,如微信,QQ,微博等.写这篇博客主要是把微信的的分享和相关操作表达一下,分享可以包含:文字,视频,音乐,图片等分享. 分享可以有 分享给好友 ...

随机推荐

  1. No.008 String to Integer (atoi)

    8. String to Integer (atoi) Total Accepted: 112863 Total Submissions: 825433 Difficulty: Easy Implem ...

  2. MySQL学习笔记(二)

    二.SQL基本知识 SQL 是一种典型的非过程化程序设计语言,这种语言的特点是:只指定哪些数据被操纵,至于对这些数据要执行哪些操作,以及这些操作是如何执行的,则未被指定.非过程化程序设计语言的优点在于 ...

  3. PZISP自动下载软件运行时出现“应用程序无法启动,因为应用程序的并行配置不正确”

    在win7下以管理员身份运行“PZISP自动下载软件”时出现“应用程序无法启动,因为应用程序的并行配置不正确”时,是因为系统里面没有一些visual c++库 想一想,反正以后也要用上VS2010的, ...

  4. 如何解决SWAT模型数据移动目录后出现的“SWAT2005.mdb database specified in your MasterProgress table does not exists. Please correct and try again”的问题

    方法: 1.用MS Access软件打开SWAT模型工程文件的数据文件,如“**流域模拟.mdb”,该文件一般存放在工程文件“**流域模拟.mxd”相同的路径: 2.打开以后,找到“MasterPro ...

  5. Sql server 数据库 int 和guid 两者的比较

    我们公司的数据库全部是使用GUID做主键的,很多人习惯使用int做主键.所以呢,这里总结一下,将两种数据类型做主键进行一个比较. 使用INT做主键的优点: 1.需要很小的数据存储空间,仅仅需要4 by ...

  6. .NET 可选择的转型路径(转)

    新的一年,您在学习和工作上,想坚守所学,还是尝试转型呢? (1) 继续钻研 C# 4.0 / AJAX / LINQ to AD.LINQ to JavaScript / WF, WCF, WPF, ...

  7. js控制ul的显示隐藏,对象的有效范围

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  8. Android IOS WebRTC 音视频开发总结(五四)-- WebRTC标准之父谈WebRTC

    本文主要是整理自国内首届WebRTC大会上对Daniel的一些专访,转载必须说明出处,欢迎关注微信公众号blacker,更多说明详见www.rtc.help 说明:以下内容主要整理自InfoQ的专访, ...

  9. projecteuler Sum square difference

    The sum of the squares of the first ten natural numbers is, 12 + 22 + ... + 102 = 385 The square of ...

  10. Send User to a Portal Folder

    Sometimes you would want to give users the option to click a button on the page and send them back t ...