kingslay / KSPlayer

A video player for iOS、macOS、tvOS、visionOS , based on AVPlayer and FFmpeg, support the horizontal, vertical screen. support adjust volume, brightness and seek by slide, SwiftUI, support subtitles.
https://apps.apple.com/app/tracyplayer/id6450770064
GNU General Public License v3.0
980 stars 198 forks source link

修复字幕未及时清除的问题 #774

Closed FantasyKingdom closed 5 months ago

FantasyKingdom commented 5 months ago

有时候解码出字幕,但是字幕无内容也需要及时刷新。 不然会出现人物已经很久没说话了,但是字幕却一直显示的问题。

这个修改主要是用来处理preSubtitleFrame.part.end == preSubtitleFrame.part.start的这种没有持续时间的字幕,可以再加一个preSubtitleFrame.part.end == preSubtitleFrame.part.start判断,不加其实也没有影响。

题外话: 第一段代码 extension FFmpegAssetTrack: KSSubtitleProtocol { public func search(for time: TimeInterval) -> [SubtitlePart] { subtitle?.outputRenderQueue.search { item -> Bool in item.part < time || item.part == time }.map(.part) ?? [] } } 第二段代码: if let subtile = selectedSubtitleInfo { let currentTime = currentTime - subtile.delay - subtitleDelay newParts = subtile.search(for: currentTime) if newParts.isEmpty { newParts = parts.filter { part in part.end <= part.start || part == currentTime } } }

我觉得对于那种没有duration的字幕,视频文件中应该会提供一个空字幕来进行清除(我提交的这段代码)。 可以把duration设置为无限长或者长一点的时间比如5秒,然后用空字幕进行清除。 而不用像第一段代码和第二段代码那样去判断item.part < time或者是part.end <= part.start。以及preSubtitleFrame这个都可以去掉了。

kingslay commented 5 months ago

你可以提供下视频片段让我来测试下吗? 我的代码里面的这一段,正常来说就可以用当前的时间来为上一个字幕设置结束时间的。

 if let preSubtitleFrame, preSubtitleFrame.part.end == preSubtitleFrame.part.start {
            preSubtitleFrame.part.end = start
        }
kingslay commented 5 months ago

我在main分支,修复这个问题了。你可以试下。其实是因为这个字幕会输出两个时间片段一摸一样的字幕片段。我加了一个判断,把它过滤掉了。这样preSubtitleFrame就起作用了。

FantasyKingdom commented 5 months ago

你的修改并没有解决问题。 解码出来的字幕不是严格的时间顺序的 所以后一个字幕的start时间可能比前面一条字幕的start还要小,导致end设置了一个比start小的值,这时如果没有新的空字幕,而只依赖上述的第二段代码是无法清除掉当前字幕的。我提交的代码可以解决这个问题。

图中的xxxx是SubtitleDecode中的字幕start时间。 当前已经播放到22秒了,仍显示12.47的字幕 image

FantasyKingdom commented 5 months ago

12.513和12.512是两条无内容的字幕。 因为12.471这条字幕的end值已经被设置为了12.47。所以不会更新它的end值。 导致12.471这条字幕一直被显示。

kingslay commented 5 months ago

不好意思。因为那个url播放太卡了。所以我是通过ffmpeg来下载一分钟的片段进行测试。ffmpeg对于下载下来的字幕时间戳应该是有做了重新矫正。所以我本地只要用等于进行判断就不会有问题了。但是线上的url是要用>=来进行判断。我改好了。麻烦你在试下。

FantasyKingdom commented 5 months ago

这样改虽然可以解决这个问题,但是我感觉某些情况下会把一部分正常的字幕给漏掉了。

我觉得就不应该使用preSubtitleFrame这个对象,可以去掉,而是将解码出来的空字幕放到字幕的缓冲区会比较好,像第一个评论说的那样。当然,这仅是我自己的看法。

kingslay commented 5 months ago

可以试着把preSubtitleFrame给去掉。但是对于开始时间和结束时间一样的字幕。还是需要item.part < time或者是part.end <= part.start来进行判断,不能直接用一个固定的长度 5s。不然对于文字字幕的话,那有可能在某个时间段同时显示两个文字字幕。

FantasyKingdom commented 5 months ago

我把preSubtitleFrame去掉了,然后这样改从逻辑上应该是没有你说的这个问题的

image image
kingslay commented 5 months ago

嗯,你这样改是可以的。而且如果是空字幕应该不需要返回一个空的字幕了。因为之前是有item.part < time 这个判断,所以才可以返回空字幕进行清空。现在是根据 item.part == time来判断了。空字幕应该是无法返回了。你可以去掉这个代码试下。应该也是没有问题的。 image

FantasyKingdom commented 5 months ago

即使是空字幕,也是有设置时间的,把它当作正常字幕即可,只是无内容。 按照我的这种改法那parts.append(SubtitlePart(0, 0, attributedString: nil))这一句是不能少的。

image
kingslay commented 5 months ago

嗯,我按照你的思路改好了。你的思路确实更好。返回一个空字幕是一个正确的方式。这样才能解决字幕解码不按顺序的问题。你可以试下

FantasyKingdom commented 5 months ago

是的,这样改就没问题了。既能解决字幕重复字幕及乱序问题,而且整体逻辑也更加清晰了。之前的各种<=判断在第一次看代码的时候真的很让人迷惑😄。