Fork me on GitHub

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

本文的字幕文件解析代码参考了开源代码 ramtinakhttps://github.com/ramtinak/UltraPlayer

posted @ 2020-06-17 18:30  猫叔Vincent  阅读(448)  评论(0编辑  收藏  举报