UWP实现第二字幕并且跟随系统的设置
话不多说,先看一下最终效果
系统设置默认

在系统设置里面更改字幕的显示效果

需求
要求播放器可以显示第二字幕,类似旁白的文字解释。比如片中出现了一个专业术语,这个时候观众可能有些疑惑。所以需要在屏幕上显示这个专业术语的解释。
1. 解析字幕文件
第二字幕也是字幕文件,需要找专门的类进行解析。而第一字幕则不需要这么麻烦,播放器会自动处理并显示的。
srt字幕文件一般格式如下
- 字幕序号
- 字幕显示的起始时间 --> 结束时间
- 字幕内容(可多行)
- 空白行(表示本字幕段的结束)
字母序号并不起任何实际的作用,只是用来标明而已,解析的时候用不到

首先加载一个字幕文件,我把文件放在Assets文件夹里面了。
var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/secondCC.srt"));
if (file == null)
return;
Stream stream = await file.OpenStreamForReadAsync();
var parser = new SubParser();
SubtitleList = parser.ParseStream(await file.OpenStreamForReadAsync(), encoding, mostLikelyFormat);
代码通过读取srt文件,把内容都存在一个List<SubtitleItem>,
SubtitleItem的model定义为
public int Number { get; set; }
public int StartTime { get; set; }
public int EndTime { get; set; }
public List<string> Lines { get; set; }


2. 获取系统字幕设置
打开Windows设置——轻松使用——隐藏式字幕
默认情况下所有的设置都是默认,当然你可以自己更改,不过这个将对你的第一字幕产生影响。而我们要达到的效果是同时更改第二字幕的效果。
比如获取字体颜色
if (Windows.Media.ClosedCaptioning.ClosedCaptionProperties.FontColor != Windows.Media.ClosedCaptioning.ClosedCaptionColor.Default)
richtextblock.Foreground = new SolidColorBrush(Windows.Media.ClosedCaptioning.ClosedCaptionProperties.ComputedFontColor);
else
richtextblock.Foreground = new SolidColorBrush(Colors.White);
字体大小
//系统默认不返回字体的具体大小,而是一个愚蠢的百分比。官方解释说具体的字体大小会根据窗体大小等一系列因素决定,但是又不给你说怎么个计算方法
//所以这里就先给一个初始值。如果你知道怎么计算或者获取最终大小,请create PR。
double defaultSize = ;
switch (Windows.Media.ClosedCaptioning.ClosedCaptionProperties.FontSize)
{
case Windows.Media.ClosedCaptioning.ClosedCaptionSize.FiftyPercent:
richtextblock.FontSize = defaultSize * .;
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionSize.OneHundredPercent:
richtextblock.FontSize = defaultSize * ;
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionSize.OneHundredFiftyPercent:
richtextblock.FontSize = defaultSize * 1.5;
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionSize.TwoHundredPercent:
richtextblock.FontSize = defaultSize * 2.0;
break;
default:
richtextblock.FontSize = defaultSize * 1.0;
break;
}
背景色
if (Windows.Media.ClosedCaptioning.ClosedCaptionProperties.BackgroundColor != Windows.Media.ClosedCaptioning.ClosedCaptionColor.Default)
{
border.Background = new SolidColorBrush(Windows.Media.ClosedCaptioning.ClosedCaptionProperties.ComputedBackgroundColor); Color backColor = Windows.Media.ClosedCaptioning.ClosedCaptionProperties.ComputedBackgroundColor;
switch (Windows.Media.ClosedCaptioning.ClosedCaptionProperties.BackgroundOpacity)
{
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.OneHundredPercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));//.Opacity = 1.0;
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.SeventyFivePercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.TwentyFivePercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.ZeroPercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
default:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
}
}
else
{
Color backColor = Colors.Black;
switch (Windows.Media.ClosedCaptioning.ClosedCaptionProperties.BackgroundOpacity)
{
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.OneHundredPercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));//.Opacity = 1.0;
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.SeventyFivePercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.TwentyFivePercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.ZeroPercent:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
default:
border.Background = new SolidColorBrush(Color.FromArgb(, backColor.R, backColor.G, backColor.B));
break;
}
}

3. 显示第二字幕
在timer里面,我们需要实时更新字幕内容。
如果字幕文件有自定义的样式,那么最终的样式将会呗保留,而不受系统影响。
try
{
if (SubtitleList != null && SubtitleList.Any())
{
var v = (from item in SubtitleList
where item != null
&& item.StartTime + seekDouble <= MyPlayer.MediaPlayer.PlaybackSession.Position.TotalMilliseconds
&& item.EndTime + seekDouble >= MyPlayer.MediaPlayer.PlaybackSession.Position.TotalMilliseconds
orderby item descending
select item).FirstOrDefault();
CurrentSubtitleItem = v;
if (v != null)
{
richtextblock.Blocks.Clear(); Paragraph myParagraph = new Paragraph();
int nextParagraph = ;
string paragraph = "";
foreach (string item in v.Lines)
{
paragraph += item.Trim().ToString() + "\r\n";
if (GetRun(item) != null)
{
myParagraph.Inlines.Add(GetRun(item.Trim()));
try
{
if (v.Lines[nextParagraph] != null)
{
myParagraph.Inlines.Add(new LineBreak());
}
}
catch (Exception ex) { Debug.WriteLine("nextParagraph ex: " + ex.Message); }
}
nextParagraph++;
}
//Run run = new Run();
//run.Text = paragraph.Trim();
//myParagraph.Inlines.Add(run);
richtextblock.Blocks.Add(myParagraph);
border.Visibility = Visibility.Visible;
}
else
{
border.Visibility = Visibility.Collapsed;
richtextblock.Blocks.Clear();
}
}
else
richtextblock.Blocks.Clear();
}
catch (Exception ex) { Debug.WriteLine("mediaPlayer_PositionChanged ex: " + ex.Message); }
比如字幕文件有
::, --> ::,
::,000When a powerful desire indwells in things touched by mortal souls,When a powerful desire indwells in things touched by mortal souls,
<font color=red>颜色</font>
<i>字体斜体</i>
<u>字体下加划线</u>
<br>换行
<b>字体加粗</b>
::, first line ends ::, --> ::,
::, they become goblins.
那么最终的展示效果为:
紫色效果是系统设置,而颜色在字幕里面内置了红色,那么它将不会受系统影响。



4. 源代码
本代码已经开源,获取请点击,如果可以的话,请点击右上角Star
https://github.com/hupo376787/UWPSecondSubtitle
5. 特别鸣谢
开源动画组织:bbb_sunflower_1080p_60fps_normal.mp4/elephantsdream-clip-h264_sd-aac_eng-aac_spa-aac_eng_commentary-srt_eng-srt_por-srt_swe.mkv
本文的字幕文件解析代码参考了开源代码 ramtinak:https://github.com/ramtinak/UltraPlayer
UWP实现第二字幕并且跟随系统的设置的更多相关文章
- [置顶] iOS 应用程序内部国际化,不跟随系统语言
前言:网络上关于iOS国际化的文章很多,但基本上都是基于跟随系统语言的国际化,笔者就不赘述了-0 – 今天要讲的是不跟随系统的切换语言版本方案,即程序内部的切换语言版本方案. 一.总则: 应用内部语言 ...
- 【Windows 10 应用开发】跟随系统主题颜色
有些时候,希望应用程序中的某些颜色可以与系统的主题颜色相同,并且当系统主题色改变时进行同步. 实现过程并不复杂,主要用到 UISettings 类,它公开一个 GetColorValue 方法,访问这 ...
- Android: 设置 app 字体大小不跟随系统字体调整而变化
在做 app 内字体大小的需求,类似于 微信中设置字体大小. 那么就需要 app 不跟随系统字体大小调整而变化,找到了两个方法. 方法1: 重写 getResource() 方法,修改 configu ...
- iOS 应用程序内部国际化,不跟随系统语言
前言:网络上关于iOS国际化的文章很多,但基本上都是基于跟随系统语言的国际化,笔者就不赘述了-0 – 今天要讲的是不跟随系统的切换语言版本方案,即程序内部的切换语言版本方案. 一.总则: 应用内部语言 ...
- 网页跟随系统 dark mode (暗黑模式) 的实现
经过几十年的沉默, dark mode(暗黑模式) 又回到了我们面前,越来越多的 APP 有了暗黑主题,越来月多的操作系统原生添加了 "全局暗黑模式", 那么一个网站如何跟随系统的 ...
- Linux下锁定账号,禁止登录系统的设置总结【转】
在我们运维工作中,会经常要求一些用户不允许登陆系统,以加固系统安全.今天这里介绍下锁定账号登陆的几种方法: (推荐使用)这种方式会更加人性化一点,因为不仅可以禁止用户登录,还可以在禁用登陆时给提示告诉 ...
- 第三部分:Android 应用程序接口指南---第二节:UI---第五章 设置(Settings)
第5章 设置(Settings) 应用程序通常包括允许用户修改应用程序的特性和行为的设置功能.例如,一些应用程序允许用户指定通知是否启用或指定多久使用云同步数据.如果你想要为你的应用程序提供设置,你应 ...
- Ubuntu14.04、win7双系统如何设置win7为默认启动项
Ubuntu14.04.win7双系统如何设置win7为默认启动项 Ubuntu14.04.win7双系统设置win7为默认启动项方法: 在启动项选择菜单处记住windows 7对应的序号. 从上至下 ...
- iOS 跳转到系统的设置界面
跳到健康设置 上网找了一下 你会发现很难找到.代码如下 不信你试试 . NSURL *url = [NSURL URLWithString:@"prefs:root=Privacy& ...
随机推荐
- ExtJS按钮
var toppanel = Ext.create('Ext.panel.Panel',{ layout : { type : 'absolute' }, bodyStyle : { backgrou ...
- Altera的Cyclone系列器件命名规则
Altera的Cyclone系列器件命名规则如下 器件系列 + 器件类型(是否含有高速串行收发器) + LE逻辑单元数量 + 封装类型 + 高速串行收发器的数量(没有则不写) + 引脚数目 + 器件 ...
- 【解构云原生】初识Kubernetes Service
编者按:云原生是网易杭州研究院(网易杭研)奉行的核心技术方向之一,开源容器平台Kubernetes作为云原生产业技术标准.云原生生态基石,在设计上不可避免有其复杂性,Kubernetes系列文章基于网 ...
- Docker 入门:Dockerfile
主要内容: 什么是 Dockerfile 查看 DockerHub 中镜像的 Dockerfile Dockerfile 编写 Dockerfile 常用命令 什么是 Dockerfile 使用 Do ...
- JavaScript的历史由来及简介
JavaScript的历史由来及简介 前言 这次写一篇对于JavaScript的简介,我们知道的编程语言有很多种,比如Java.C++.Python等等,每种编程语言都有其独具的特色,不论是语法格式还 ...
- 50个SQL语句(MySQL版) 问题七
--------------------------表结构-------------------------- student(StuId,StuName,StuAge,StuSex) 学生表 tea ...
- webpack+vue2.0项目 (二)热加载,vue-router
目录创建好之后,命令行输入 npm run dev 因为在配置文件config/index.js里: dev: { env: require('./dev.env'), port: 8080, aut ...
- Java实现 LeetCode 762 二进制表示中质数个计算置位(位运算+JDK的方法)
762. 二进制表示中质数个计算置位 给定两个整数 L 和 R ,找到闭区间 [L, R] 范围内,计算置位位数为质数的整数个数. (注意,计算置位代表二进制表示中1的个数.例如 21 的二进制表示 ...
- Java实现 LeetCode 709 转换成小写字母(ASCII码处理)
709. 转换成小写字母 实现函数 ToLowerCase(),该函数接收一个字符串参数 str,并将该字符串中的大写字母转换成小写字母,之后返回新的字符串. 示例 1: 输入: "Hell ...
- Java实现 蓝桥杯 算法训练 未名湖边的烦恼
算法训练 未名湖边的烦恼 时间限制:1.0s 内存限制:256.0MB 问题描述 每年冬天,北大未名湖上都是滑冰的好地方.北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩. ...