Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

弹幕xml文件末尾缺失<i>标签 #452

Open
actck opened this issue Jan 19, 2023 · 22 comments
Open

弹幕xml文件末尾缺失<i>标签 #452

actck opened this issue Jan 19, 2023 · 22 comments
Labels
area: Core bug Something isn't working need-more-info

Comments

@actck
Copy link

actck commented Jan 19, 2023

Platforms

WPF (桌面版)

Version

v2.5.0

Description

弹幕xml文件末尾缺失标签,
在录屏因为网络等原因分段的时候, 任一视频文件对应的弹幕xml文件末尾缺失i标签, 2022年录播四百多场发现了二十多场是这样子的,最新有问题的一次录播出现在2022年11月29, 也就是说最新版本也有这个问题
QQ20230119-192645@2x

Logs

No response

@Genteure
Copy link
Member

对应场次的日志文件还留着吗?发一下

@actck
Copy link
Author

actck commented Jan 19, 2023

您好 请问是有写磁盘日志吗, 麻烦给下磁盘路径, 我好像没看到日志路径配置

@actck
Copy link
Author

actck commented Jan 19, 2023

我找到路径了 但是日志你这边似乎之保留了30天 已经被清理了

@Genteure
Copy link
Member

在房间列表页面文件菜单里可以打开,或者 %localappdata%\BililiveRecorder\app-2.5.0\logs

@Genteure
Copy link
Member

你是在什么设备上运行的?24*7运行的NAS?云服务器?还是自己日常使用的电脑?
平常会关机重启吗?会重启录播姬吗?有没有可能是直接退出了录播姬导致的文件没有正常关闭?

在录屏因为网络等原因分段的时候

你是怎么知道是因为网络问题分段的?后面一个文件和有问题的弹幕对应的视频文件内容是连续的吗?

@actck
Copy link
Author

actck commented Jan 19, 2023

  1. 服务器运行, VMware, win10 LTSC, epyc服务器
  2. 不是意外关机导致 所有的录屏文件都错开了服务器维护时间
  3. 因为所有的出现这个问题的录屏场次, 无一例外都是分段的, 我不确定是不是网络原因 但是 是分段的
  4. 出现问题的文件, 后面一个分段视频文件对应的xml是完整的, 有完整的xml起始标签, 不像是写入过程中意外停机被断掉

@actck
Copy link
Author

actck commented Jan 19, 2023

出现问题的直播场次 几乎都出现了>=3段以上的分段, 如果真的是服务器重启不会说连续重启多次, 所以我猜测只能是网络 或者主播频繁开关播 或其他什么原因分段触发的问题

@Genteure Genteure added bug Something isn't working area: Core need-more-info labels Jan 19, 2023
@Genteure
Copy link
Member

Genteure commented Jan 19, 2023

录播输出目录是 VM 本地磁盘吗?还是网络磁盘挂载进去的?如果是挂载的网络磁盘,有没有可能是文件写入出问题了?文件写入报错导致的断开重试,所以弹幕文件也没有正常关闭。

如果不是网络磁盘,就再观察观察吧,再出现这个情况了之后看一下日志。

@actck
Copy link
Author

actck commented Jan 19, 2023

确实是网络磁盘挂载的, 但是网络部分比服务器还稳定 uptime已经几个月了

不过就算是网络磁盘 应该和本地vm一样的吧, 系统不是屏蔽了这部分差异么, 如果文件流flush失败程序应该都会有异常

我观察一下吧, 下次出现附上日志

@actck
Copy link
Author

actck commented Jan 19, 2023

11月29日这场直播我发现了一个分段的txt说明, "收到直播服务器发送的 onMetaData 数据,请检查此位置是否有重复的直播片段或缺少数据。造成这个问题的原因可能是录播姬所连接的直播服务器与它的上级服务器的连接断开重连了。"

附件里的txt与问题的弹幕xml是相同分段, 至少应该可以证明不是磁盘系统的问题?

录制-2064239-20221129-205558-203-治愈小狗!不信你尝尝!.txt

@Genteure
Copy link
Member

这个不能证明不是磁盘导致的断开,这个文件反而证明没有因为这个问题而分段。

设置里有一个开关,目前默认是收到 onMetaData 之后记录到这个 txt 文件里,设置里可以改成收到了之后分段。有这个文件说明不是因为这个原因分段的。这个也不是与直播服务器的连接断开了。

@actck
Copy link
Author

actck commented Jan 19, 2023

嗯 我看了配置这个开关没有打开, 等下次出现我补充一下日志吧 辛苦了

@Genteure
Copy link
Member

录播文件断开有几个原因:

  • 与直播服务器网络连接异常断开(软件:本地杀软防火墙、互联网波动、B站直播服务器问题等;硬件:网线断了、WiFi不稳、路由器挂了等)
  • 直播服务器正常关闭连接(直播正常结束主播结束推流、没有结束直播但是直播服务器有问题关闭连接)
  • 文件写入错误(本地IO问题、磁盘满了等)
  • 标准录制模式下修复系统为了修数据断开(直播流里编码参数变动)
  • 连续 10 秒没有处理新的直播数据,可能因为网络问题、直播服务器问题、或文件写入问题导致,防止卡死会强制结束。

所有原因导致文件关闭的时候都会去尝试结束弹幕文件,所以弹幕文件没有正常结束应该是尝试关闭弹幕文件的时候弹幕文件就已经不能写了。

@123485k
Copy link
Contributor

123485k commented Feb 6, 2023

我很早就反应过这个问题,在录制过程中的xml文件缺少</i>,如果能保证每次写入都是生成的完整的弹幕文件,需要付出很大的代价吗

@Genteure
Copy link
Member

Genteure commented Feb 7, 2023

首先要知道是什么原因导致的

@123485k
Copy link
Contributor

123485k commented Feb 9, 2023

首先要知道是什么原因导致的

就是录播姬在写入弹幕xml的时候只有在关闭文件的时候才会写入</i>,如果在每次写入弹幕的时候都加上</i>标签,就算录播姬以外关闭,也是完整的文件。

@Genteure
Copy link
Member

Genteure commented Feb 9, 2023

如果在每次写入弹幕的时候都加上 </i> 标签

这个实现不了,用的是 .NET 的 XmlWriter 只能一个一个 element 写。
就算能实现,每次写数据都多写一点再 seek 回去覆盖掉只为了避免很少见的这个问题感觉也不太合理,性能低。

@oxygenkun
Copy link

如果不用xml格式存储,而是定义一种log格式,录播完成后转换成xml,比较可靠。

@Genteure
Copy link
Member

如果不用xml格式存储,而是定义一种log格式,录播完成后转换成xml,比较可靠。

最近确实又在考虑类似这样的设计了,存到数据库里之类的。不过要不要做、具体怎么实现之类的还没想明白。以前曾经有过一个大饼 #185 但是后来咕了。

@actck
Copy link
Author

actck commented Feb 14, 2023

我不知道.net的框架是怎么样的,但是以java的开发经验,我觉得不至于一个面向对象的xml框架会以这种最原始直接append的方式写文本文件吧?

@Genteure
Copy link
Member

我不知道.net的框架是怎么样的,但是以java的开发经验,我觉得不至于一个面向对象的xml框架会以这种最原始直接append的方式写文本文件吧?

.NET 里面的 XML 处理有 3 套不同层面的 API

  1. XmlSerializer: 把 XML 数据直接反序列化成强类型的 object,和把强类型的 object 序列化成 XML 数据。

    比如录播姬FLV数据处理系统里,模拟修复部分就在用

    XmlFlvFile.Serializer.Serialize(file, new XmlFlvFile { Tags = w.Files[i] });

    用这个 API 的时候,只需要考虑这个数据在运行时的结构,读写过程是个黑箱(虽然可以用 attribute 控制)。

  2. XmlDocument: 把 XML 数据解析加载成一个 Document,Document 里有 Node,类似浏览器的 DOM 的感觉。可以直接操作修改各个 Node 然后再输出 XML 数据。

    用这个 API 的时候,考虑的是 XML 文档和各个 Node,使用的 API 类似浏览器 DOM API。

  3. XmlWriterXmlReader: 低层的、直接的、单向的读写 XML 数据。

    用这个 API 的时候,考虑的是文件里的具体文本内容,比如 “开始一个 element” “结束一个 element” “写入注释”。
    它们会自动处理字符串的转义、检查层级,保证输出/读取的 XML 数据是合法的。

这三种 API 都能保证输出的是符合标准的 XML 数据,该转义的字符全部被转义。如果是自己用文本拼接的方式写的话就不能保证。

就录播姬录制弹幕的这个功能来说,最合理的是第 3 个 API XmlWriter

使用前两个 API 都需要把整个文档保存在内存中,一次性写入磁盘。

// 伪代码

var 弹幕列表 = [];

while(直播中)
{
  var 弹幕 = 读弹幕();
  弹幕列表.push(弹幕);
}

写文件(弹幕列表);

这样有两个缺点:

  • 需要一次性写入大量数据,会等很长时间磁盘 IO,影响后续或其他录制任务。
  • 需要在内存中保存当次录制的弹幕数据,内存占用大。

现在录播姬录制弹幕的过程是类似这样

// 伪代码

写文件(文件头);

while(直播中)
{
  var 弹幕 = 读弹幕();
  写文件(弹幕);
}

写文件(文件尾);

优点:

  • 磁盘写入压力分散到整个录制过程
  • 收到弹幕后就写入文件,不需要在内存里保留

缺点:

  • 如果文件没有被录播姬正常关闭,可能会缺少文件尾

writer.WriteStartDocument();
writer.WriteProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"#s\"");
writer.WriteStartElement("i");
writer.WriteComment("\nB站录播姬 " + GitVersionInformation.InformationalVersion + "\nhttps://rec.danmuji.org/user/danmaku/\n本文件的弹幕信息兼容B站主站视频弹幕XML格式\n本XML自带样式可以在浏览器里打开(推荐使用Chrome)\n\nsc 为SuperChat\ngift为礼物\nguard为上船\n\nattribute \"raw\" 为原始数据\n");
writer.WriteElementString("chatserver", "chat.bilibili.com");
writer.WriteElementString("chatid", "0");
writer.WriteElementString("mission", "0");
writer.WriteElementString("maxlimit", "1000");
writer.WriteElementString("state", "0");
writer.WriteElementString("real_name", "0");
writer.WriteElementString("source", "0");
writer.WriteStartElement("BililiveRecorder");
writer.WriteAttributeString("version", GitVersionInformation.FullSemVer);
writer.WriteEndElement();
writer.WriteStartElement("BililiveRecorderRecordInfo");
writer.WriteAttributeString("roomid", room.RoomConfig.RoomId.ToString());
writer.WriteAttributeString("shortid", room.ShortId.ToString());
writer.WriteAttributeString("name", RemoveInvalidXMLChars(room.Name));
writer.WriteAttributeString("title", RemoveInvalidXMLChars(room.Title));
writer.WriteAttributeString("areanameparent", RemoveInvalidXMLChars(room.AreaNameParent));
writer.WriteAttributeString("areanamechild", RemoveInvalidXMLChars(room.AreaNameChild));
writer.WriteAttributeString("start_time", DateTimeOffset.Now.ToString("O"));
writer.WriteEndElement();


因为 XML 文件可能会很大,第三方工具处理的时候直接加载到内存里,在部分小内存设备上可能会不够用。所以处理超大文件最好还是用 XmlReader 这样的 API 流式处理。比如录播姬工具箱里的弹幕合并功能就是流式处理的,一次只读一个 element。

因为 XML 有转义的,解析的时候最好是用专门的 XML Parser 来解析(不管是否是流式),比如浏览器里的 DOMParser。但比如某开源网页播放器的弹幕插件,实际测试发现用正则匹配解析的速度比用 DOMParser 快很多很多,绝对的正确性也不是很重要(大不了就是画面上弹幕显示错两个),就直接用正则匹配+简单的手动反转义了。再加上有些其他的视频下载工具之类生成的 XML 弹幕文件是手动拼接的,字符转义没有处理好,使用标准的严格的 XML Parser 会报错。

说回来 XML 这个格式存弹幕本身就不是很好,纯粹是因为有很多现成的工具支持这个格式、是个事实标准所以才用的。如果抛弃这个原因,有很多其他更合理的文件格式可选,使用的硬盘空间也能更小。

@actck
Copy link
Author

actck commented Feb 14, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: Core bug Something isn't working need-more-info
Development

No branches or pull requests

4 participants