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 = 50; switch (Windows.Media.ClosedCaptioning.ClosedCaptionProperties.FontSize) { case Windows.Media.ClosedCaptioning.ClosedCaptionSize.FiftyPercent: richtextblock.FontSize = defaultSize * .5; break; case Windows.Media.ClosedCaptioning.ClosedCaptionSize.OneHundredPercent: richtextblock.FontSize = defaultSize * 1; 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(255, backColor.R, backColor.G, backColor.B));//.Opacity = 1.0; break; case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.SeventyFivePercent: border.Background = new SolidColorBrush(Color.FromArgb(192, backColor.R, backColor.G, backColor.B)); break; case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.TwentyFivePercent: border.Background = new SolidColorBrush(Color.FromArgb(64, backColor.R, backColor.G, backColor.B)); break; case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.ZeroPercent: border.Background = new SolidColorBrush(Color.FromArgb(0, backColor.R, backColor.G, backColor.B)); break; default: border.Background = new SolidColorBrush(Color.FromArgb(0, 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(255, backColor.R, backColor.G, backColor.B));//.Opacity = 1.0; break; case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.SeventyFivePercent: border.Background = new SolidColorBrush(Color.FromArgb(192, backColor.R, backColor.G, backColor.B)); break; case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.TwentyFivePercent: border.Background = new SolidColorBrush(Color.FromArgb(64, backColor.R, backColor.G, backColor.B)); break; case Windows.Media.ClosedCaptioning.ClosedCaptionOpacity.ZeroPercent: border.Background = new SolidColorBrush(Color.FromArgb(0, backColor.R, backColor.G, backColor.B)); break; default: border.Background = new SolidColorBrush(Color.FromArgb(0, 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 = 1; 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); }
比如字幕文件有
1 00:00:00,000 --> 00:00:15,351 00:00:00,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> 😘😘😘00:01:02,351 first line ends😘😘😘 2 00:00:15,351 --> 00:01:03,881 00:00:15,351 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