【Win 10 应用开发】在后台进行多媒体转码
前面,老周给大伙儿讲了如何运用 MediaTranscoder 类来完成多媒体。然而,你懂的,要是多媒体文件比较大,转码时间会更长,有可能用户不会一眭停在当前应用界面上,或许会切换到其他应用程序,甚至关闭应用程序。
所以,在特定情形下,是得考虑在后台任务上进行转码,这样哪怕前台应用退出了,也不会使转码中止,处理工作可以在后台任务中继续耍。
不过,多媒体转码这玩意儿毕竟很耗性能,也很耗电,强烈建议大家不要搞什么批量转码,不然,我估计平板/手机的电池续航能力支撑不了。
示例我已经做好了,因而完整的代码大伙儿可以下载来慢慢参考,bug比较多,因为老周的编程水平实在太差,比不上支付婊的高手们,故bug如林。
下面老周就重点扯一下实现过程。其实也没什么,也是用到万能的 MediaTranscoder 类,只不是过放在后台任务中进行了而已。
1、实现后台任务。咱们先做后台吧,在新建项目时,一定要选“Windows 运行时组件”,以前有朋友错选了类库项目,结果一运行就呵呵了。所以,选错老婆是很痛苦的,记好了,是 windows 运行时组件。
不要把后台类型声明在主启动项目中,不然激活后台时会把你的前台程序给挂掉,因为运行后台是要新的实例的,而RT应用为了防止人品不端正的开发者故意破坏人民群众的公共设施,RT一般同时只能有一个实例在运行,所以,后台类一定要扔到一个独立的 Windows 运行时组件中,并在主启动项目中引用之。
实现后台,我相信大家都会了,老周以前也写过相关的东东,而且老周的破书里面也有讲到。总的一句就是:实现IBackgroundTask接口,就完事了。
好了,上代码。
public sealed class MPBackTask : IBackgroundTask
{
public async void Run(IBackgroundTaskInstance taskInstance)
{ 。。。。。。。。。。。。。。。。。。 } private async Task TranscodeAsync(IBackgroundTaskInstance taskInst, MediaProcessingTriggerDetails d)
{
。。。。。。。。。。。。。。。。。。。。。。。。。
}
}
Run方法是必须实现的,因为接口中包含了该方法,而TranscodeAsync方法是我自定义的,负责完成转码工作。
先看Run方法,
public async void Run(IBackgroundTaskInstance taskInstance)
{
var deferral = taskInstance.GetDeferral();
var details = taskInstance.TriggerDetails as MediaProcessingTriggerDetails; try
{
// 向应用设置写入一个标记,表示后台正在处理转码
// 数据内容随意
ApplicationData.Current.LocalSettings.Values["running"] = "y"; await TranscodeAsync(taskInstance, details);
ShowNotification("后台转码成功。");
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"异常:{ex.Message}");
}
finally
{
// 删除设置中的标记
var settingcont = ApplicationData.Current.LocalSettings;
if (settingcont.Values.ContainsKey("running"))
{
settingcont.Values.Remove("running");
}
deferral.Complete();
}
}
从taskInstance.TriggerDetails属性中你可以得到一个MediaProcessingTriggerDetails实例,有啥用呢,它可以接收从前台应用程序发送来的参数。比如输入文件,输出文件等,因为选择文件的操作肯定在前台完成的,选择文件后,在触发后台任务时传递进来。
为了让前台应用能够确认这个后台任务是否正在执行转码,我这里巧用一下应用程序设置容器,这些数据是保存在注册表中的,当你的应用被卸载时,系统会自动清理注册表,所以RT应用是很干净的,别担心。
我的做法是,在执行转码时,向设置项中加一个叫running的项,value随便,因为我不是判断值的,而是看key的,在执行转码前向设置容器加上这个key,当转码完成后再把这个key干掉。这样一来,在前台应用程序中,如果发现有这个key就说明转码还在后台干活,如果没有这个key,表明转码完成了。
好,现在来看看 TranscodeAsync 方法,我让它返回Task,表示它可以异步等待,因为要接收从前台发来的参数,所以,用一个d参数来引用MediaProcessingTriggerDetails实例。那为什么还要一个参数来引用IBackgroundTaskInstance呢,这是为了报告处理进度,如果前台程序觉得有必要监视进度时,可以向前台应用报告进度。
代码如下。
private async Task TranscodeAsync(IBackgroundTaskInstance taskInst, MediaProcessingTriggerDetails d)
{
// 取出传进来的参数
string inputpath = d.Arguments["input_path"] as string;
string outputpath = d.Arguments["output_path"] as string; if (inputpath == null || outputpath == null)
{
return;
} StorageFile inputfile = await StorageFile.GetFileFromPathAsync(inputpath);
StorageFile outputfile = await StorageFile.GetFileFromPathAsync(outputpath); MediaTranscoder transcoder = new MediaTranscoder();
MediaEncodingProfile profile = MediaEncodingProfile.CreateMp3(AudioEncodingQuality.High);
// 准备转码
PrepareTranscodeResult result = await transcoder.PrepareFileTranscodeAsync(inputfile, outputfile, profile); if (result.CanTranscode)
{
Progress<double> progress = new Progress<double>(p =>
{
taskInst.Progress = Convert.ToUInt32(p);
});
// 转码
await result.TranscodeAsync().AsTask(progress);
}
}
转码的过程我就不多说了,一样的,这里我把wav格式转为mp3格式,注意你输入的wav文件不能是DTS,因为有些DTS音频文件也是把后缀弄成wav,其实与wav不同,所以要用正常的wav文件,如果是DTS,转码后的mp3文件,你只能听到“沙沙沙”的声音。
关键还是介绍一下如何获取参数,MediaProcessingTriggerDetails有一个Arguments属性,其实是一个字典数据结构的东东,key是字符串,value是object类型,因为我在前台已经把输入和输出文件作为参数传过来,所以这里可以直接从参数中得到输入和输出的文件。
string inputpath = d.Arguments["input_path"] as string;
string outputpath = d.Arguments["output_path"] as string;
然后用GetFileFromPathAsync静态方法从路径生成StorageFile实例,就可以读写了。
顺便说说,官方的SDK示例中,它是用应用设置容器来传递的,就是用户选好文件后,把文件路径存到设置项中,然后在后台中读取。而我这里用的是参数传递的方法,达到的效果一样。
后台完成后,在主启动项目中引用后台项目,记得了,别忘了引用。然后打开清单文件,在[声明]选项卡中添加一个后台任务,类型勾选“媒体处理”,别勾错了,常规和系统事件都会发生异常。XML代码如下:
<Extension Category="windows.backgroundTasks" EntryPoint="BTM.MPBackTask">
<BackgroundTasks>
<uap:Task Type="mediaProcessing" />
</BackgroundTasks>
</Extension>
最后,就是在前台代码中注册后台了。因为这个后台任务只是为了完成转码工作的,并不需要长期存在,我的想法是,最好在需要转码时注册,不用时直接取消注册。另外一个原因就是,用来触发这个后台的类只能使用一次。当开始新的转码任务时,也要重新实例化一个的。
后台转码用的是 MediaProcessingTrigger 触发器类,而且,触发后台时也是要用这个类的RequestAsync方法,只要这个方法被调用,后台任务就会触发执行。可是,你懂的,Trigger是在注册后台任务时,通过BackgroundTaskBuilder类的SetTrigger方法来设置的。正因为如此,当你每次实例化一个MediaProcessingTrigger时,都必须注册一次后台任务,否则无法与后台任务关联。但是,已经注册的后台任务是不能重复注册的,所以唯一的做法就是每用一次注册一次。
private MediaProcessingTrigger SetBackgroundTaskForTranscode()
{
BackgroundTaskBuilder tb = new BackgroundTaskBuilder();
tb.Name = "btc";
tb.TaskEntryPoint = $"{nameof(BTM)}.{nameof(BTM.MPBackTask)}";
MediaProcessingTrigger trigger = new MediaProcessingTrigger();
tb.SetTrigger(trigger);
foreach (var bt in BackgroundTaskRegistration.AllTasks)
{
if (bt.Value.Name == "btc")
{
bt.Value.Unregister(true);
break;
}
}
var r = tb.Register();
r.Progress += onProgress;
r.Completed += onCompleted;
return trigger;
}
以上方法就是用于注册后台任务的,注册后,并把关联的 MediaProcessingTrigger实例返回,以供其他代码来触发。
下面代码将触发后台任务,执行转码。
if (ApplicationData.Current.LocalSettings.Values.ContainsKey("running"))
{
tbmsg.Text = "有转码任务正在运行,请稍等。";
return;
} btnStart.IsEnabled = false;
MediaProcessingTrigger mdproctrigger = SetBackgroundTaskForTranscode(); MediaProcessingTriggerResult res = await mdproctrigger.RequestAsync(argstobtproc);
switch (res)
{
case MediaProcessingTriggerResult.Allowed:
tbmsg.Text = "已启动后台转码。";
break;
case MediaProcessingTriggerResult.DisabledByPolicy:
tbmsg.Text = "你的山寨机禁止后台运行。";
break;
default:
tbmsg.Text = "对不起,发生灵异事件。";
break;
}
还记得吧,前面在实现后台任务时,我向设置容器中写了个running的项, 这里可以检查一下,如果running存在,说明后台转码还在进行,就不要重开启操作。
RequestAsync方法调用时,可以把要传给后台的参数放进去,即一个ValueSet实例,刚才说了,其实它是一个字典。
最后,还有一个很关键的,就是访问文件的权限。RT应用不像传统桌面应用那样你可以毫不顾忌地访问各种文件。RT库对文件的访问权限有严格的控制。用户通过picker选择了一个文件,但是,这个StorageFile无法直接传给后台,因此只能把这个文件的path传到后台,再在后台中用GetFileFromPathAsync方法再获取StorageFile实例。然而,问题来了,如何让后台任务代码具备访问权限呢?
没事,SDK既然想到严格的安全控制,当然就会有解决方案。在Windows.Storage.AccessCache命名空间下,有专门的类,可以用来授权。
用户通过操作选择了文件后,会得到一个StorageFile实例,我们只需要把这个StorageFile实例 Add 到StorageApplicationPermissions类的任何一个属性的集合中就可以了。
其实,StorageApplicationPermissions类就两个属性——FutureAccessList 和 MostRecentlyUsedList,这两个属性用法是一样的,只是意思不同罢了。MostRecentlyUsedList 是过去式,表示文件访问历史;FutureAccessList 是未来式,表示即将要用到的列表。
所以,无论你把文件添加到哪个属性中,应用程序就具备该文件的访问权了。
有人会说,这不是多此一举吗,还要用这个来授权。非也,表面上看好像是多余的,但你想想,文件是如何得到的?就是通过Picker让用户自愿选择的,是吧,这就对了,如果用户不想让你访问那个文件,他就不选;只有当用户自己选了,你的代码才能访问到那个文件,才能把这个文件加入到FutureAccessList列表中。所以嘛,这样的处理不流氓,很有人品,很有德行。
下面代码把输入文件加入授权列表,输出文件的原理一样。
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add(".wav");
StorageFile wavefile = await picker.PickSingleFileAsync(); if (wavefile != null)
{
// 显示路径
tbInputfile.Text = wavefile.Path;
// 把该文件放入文件访问列表中,以便后台使用
// 授权,否则后台访问不了
StorageApplicationPermissions.FutureAccessList.Add(wavefile);
// 写入参数列表
argstobtproc["input_path"] = wavefile.Path;
}
好了,大事做完了,看看效果吧。
===============================
【Win 10 应用开发】在后台进行多媒体转码的更多相关文章
- 【Win 10 应用开发】Toast通知激活应用——前台&后台
老周最近热衷于讲故事,接下来还是讲故事时间. 有人问我:你上大学的时候,有加入过学生会吗?读大学有没有必要加入学生会? 哎哟,这怎么回答呢,从短期来说,加入学生会有点用,至少可以娱乐一下,运气好的话, ...
- 【Win 10 应用开发】多媒体转码
上次本来说好,今天咱们来讨论 socket 相关的话题,但,对于 socket ,老周还有一些问题没弄清楚,等弄清楚了,再和大伙伴们一起探讨.故,今天咱们扯一扯多媒体转码的事. 听起来挺复杂的,不过, ...
- 【Win 10 应用开发】启动远程设备上的应用
这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...
- 【Win 10 应用开发】导入.pfx证书
这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...
- 【Win 10应用开发】延迟共享
延迟共享是啥呢,这么说吧,就是在应用程序打开共享面板选择共享目标时,不会设置要共享的数据,而是等到共享目标请求数据时,才会发送数据,而且,延迟操作可以在后台进行. 这样说似乎过于抽象,最好的诠释方法, ...
- 【Win 10应用开发】Adaptive磁贴模板的XML文档结构
在若干天之前,老周给大家讲了Adaptive Toast通知的XML模板,所以相应地,今天老周给大家介绍一下Adaptive磁贴的新XML模板. 同样道理,你依旧可以使用8.1时候的磁贴模板,在win ...
- 【Win 10 应用开发】RTM版的UAP项目解剖
Windows 10 发布后,其实SDK也偷偷地在VS的自定义安装列表中出现了,今天开发人员中心也更新了下载.正式版的SDK在API结构上和以前预览的时候是一样的,只是版本变成10240罢了,所以大家 ...
- 【Win 10应用开发】认识一下UAP项目
Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...
- 【Win 10 应用开发】在代码中加载文本资源
记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...
随机推荐
- PHP搭建大文件切割分块上传功能
背景 在网站开发中,文件上传是很常见的一个功能.相信很多人都会遇到这种情况,想传一个文件上去,然后网页提示"该文件过大".因为一般情况下,我们都需要对上传的文件大小做限制,防止出现 ...
- 使用 JavaScriptService 在.NET Core 里实现DES加密算法
文章<ASP.NET Core love JavaScript>和<跨平台的 NodeJS 组件解决 .NetCore 不支持 System.Drawing图形功能的若干问题> ...
- 谈谈如何使用Netty开发实现高性能的RPC服务器
RPC(Remote Procedure Call Protocol)远程过程调用协议,它是一种通过网络,从远程计算机程序上请求服务,而不必了解底层网络技术的协议.说的再直白一点,就是客户端在不必知道 ...
- Entity Framework 6 Recipes 2nd Edition 译 -> 目录 -持续更新
因为看了<Entity Framework 6 Recipes 2nd Edition>这本书前面8章的翻译,感谢china_fucan. 从第九章开始,我是边看边译的,没有通读,加之英语 ...
- ExtJS 4.2 Grid组件的单元格合并
ExtJS 4.2 Grid组件本身并没有提供单元格合并功能,需要自己实现这个功能. 目录 1. 原理 2. 多列合并 3. 代码与在线演示 1. 原理 1.1 HTML代码分析 首先创建一个Grid ...
- SQLServer地址搜索性能优化例子
这是一个很久以前的例子,现在在整理资料时无意发现,就拿出来再改写分享. 1.需求 1.1 基本需求: 根据输入的地址关键字,搜索出完整的地址路径,耗时要控制在几十毫秒内. 1.2 数据库地址表结构和数 ...
- 原生js+css3实现图片自动切换,图片轮播
运用CSS3transition及opacity属性 制作图片轮播动画 自己这两天根据用js来控制触发CSS3中transition属性,从而写出来的以CSS3动画为基础,js控制过程的图片轮播 运用 ...
- C# await和async
基础阅读:http://www.cnblogs.com/jesse2013/p/async-and-await.html 答疑阅读:http://www.cnblogs.com/heyuquan/ar ...
- 应该是Angular2的一个bug?
为了应对未来的趋势,及时赶上下一趟互联网技术,我最近也在通过具体项目研究angular2,首先必须要吐槽的是,学习angular2的成本本身不高,但是一堆的工具.配置实在让人 很是焦灼,就像asp.n ...
- cesium自定义气泡窗口infoWindow
一.自定义气泡窗口与cesium默认窗口效果对比: 1.cesium点击弹出气泡窗口显示的位置固定在地图的右上角,默认效果: 2.对于习惯arcgis或者openlayer气泡窗口样式的giser来说 ...