前面,老周给大伙儿讲了如何运用 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 应用开发】在后台进行多媒体转码的更多相关文章

  1. 【Win 10 应用开发】Toast通知激活应用——前台&后台

    老周最近热衷于讲故事,接下来还是讲故事时间. 有人问我:你上大学的时候,有加入过学生会吗?读大学有没有必要加入学生会? 哎哟,这怎么回答呢,从短期来说,加入学生会有点用,至少可以娱乐一下,运气好的话, ...

  2. 【Win 10 应用开发】多媒体转码

    上次本来说好,今天咱们来讨论 socket 相关的话题,但,对于 socket ,老周还有一些问题没弄清楚,等弄清楚了,再和大伙伴们一起探讨.故,今天咱们扯一扯多媒体转码的事. 听起来挺复杂的,不过, ...

  3. 【Win 10 应用开发】启动远程设备上的应用

    这个功能必须在“红石-1”(build 14393)以上的系统版中才能使用,运行在一台设备上的应用,可以通过URI来启动另一台设备上的应用.激活远程应用需要以下前提: 系统必须是build 14393 ...

  4. 【Win 10 应用开发】导入.pfx证书

    这个功能其实并不常用,一般开发较少涉及到证书,不过,简单了解一下还是有必要的. 先来说说制作测试证书的方法,这里老周讲两种方法,可以生成用于测试的.pfx文件. 产生证书,大家都知道有个makecer ...

  5. 【Win 10应用开发】延迟共享

    延迟共享是啥呢,这么说吧,就是在应用程序打开共享面板选择共享目标时,不会设置要共享的数据,而是等到共享目标请求数据时,才会发送数据,而且,延迟操作可以在后台进行. 这样说似乎过于抽象,最好的诠释方法, ...

  6. 【Win 10应用开发】Adaptive磁贴模板的XML文档结构

    在若干天之前,老周给大家讲了Adaptive Toast通知的XML模板,所以相应地,今天老周给大家介绍一下Adaptive磁贴的新XML模板. 同样道理,你依旧可以使用8.1时候的磁贴模板,在win ...

  7. 【Win 10 应用开发】RTM版的UAP项目解剖

    Windows 10 发布后,其实SDK也偷偷地在VS的自定义安装列表中出现了,今天开发人员中心也更新了下载.正式版的SDK在API结构上和以前预览的时候是一样的,只是版本变成10240罢了,所以大家 ...

  8. 【Win 10应用开发】认识一下UAP项目

    Windows 10 SDK预览版需要10030以上版本号的Win 10预览版系统才能使用.之前我安装的9926的系统,然后安装VS 2015 CTP 6,再装Win 10 SDK,但是在新建项目后, ...

  9. 【Win 10 应用开发】在代码中加载文本资源

    记得前一次,老周给大伙,不,小伙伴们介绍了如何填写 .resw 文件,并且在 XAML 中使用 x:Uid 标记来加载.也顺便给大伙儿分析了运行时是如何解析 .resw 文件的. 本来说好了,后续老周 ...

随机推荐

  1. 【Machine Learning】KNN算法虹膜图片识别

    K-近邻算法虹膜图片识别实战 作者:白宁超 2017年1月3日18:26:33 摘要:随着机器学习和深度学习的热潮,各种图书层出不穷.然而多数是基础理论知识介绍,缺乏实现的深入理解.本系列文章是作者结 ...

  2. 干货分享:让你分分钟学会 JS 闭包

    闭包,是 Javascript 比较重要的一个概念,对于初学者来讲,闭包是一个特别抽象的概念,特别是ECMA规范给的定义,如果没有实战经验,很难从定义去理解它.因此,本文不会对闭包的概念进行大篇幅描述 ...

  3. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  4. Win10 IIS本地部署MVC网站时不能运行?

    异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983.html 部署后出现这个错误: 打开文件目录后发现是可以看见目录的,静态页面也是可以打开的 ...

  5. Android公共title的应用

    我们在开发Android应用中,写每一个页面的时候都会建一个title,不是写一个LinearLayout就是写一个RelativeLayout,久而久之就会觉得这样繁琐,尤其几个页面是只是标题不一样 ...

  6. Android—自定义开关按钮实现

    我们在应用中经常看到一些选择开关状态的配置文件,做项目的时候用的是android的Switch控件,但是感觉好丑的样子………… 个人认为还是自定义的比较好,先上个效果图:

  7. Linux目录结构

  8. POJ1149 PIGS [最大流 建图]

    PIGS Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 20662   Accepted: 9435 Description ...

  9. Forward+ Rendering Framework

    近几天啃各种新技术时又一个蛋疼的副产品...额,算是把AMD的Forward+ Sample抄了一遍吧. 其实个人感觉这个AMD大肆宣传的Forward+跟Intel很早之前提的Tiled-Based ...

  10. kvm上的Linux虚拟机使用virtio磁盘

    kvm上的Linux虚拟机使用virtio磁盘 系统:centos6.6  64位 网上的文章比较少,怎麽将Linux虚拟机的磁盘改为使用virtio磁盘 因为centos6或以上系统已经包含了vir ...