
最近试着写了一份简单的个人用途的转码点播服务端, 完成了之前的想法, 它能把当前工作目录下的视频转为 m3u 切片用于点播. 主要是由于部分 BDMV 视频码率过高, 很难远程播 NAS 上的内容. Jellyfin 等媒体库方案不在考虑范围.
为了实现浏览器 seek 的效果, 选择由服务端通过 PTS 时间点和 timeBase 来根据关键帧提前生成切片范围, 在请求对应切片后实时转码到 mpegts (并通过浏览器预缓冲避免播放卡顿).
但无论如何调节, 似乎偶尔都会出现音频不连续的问题, 转储后发现目标切片似乎时间和请求长度不完全一致, 调节多次参数仍未解决此问题 (为使切片有足够起始长度只测试到 muxdelay 方法可用, -ss/-t 放于输入之后似乎也无果, 包括 -start_at_zero 等的替代也都尝试过, 使用 hls 或 segment 的话较难控制自由点播进度).
由于 nodejs 服务端较难调用 FFmpeg API, 是用的 命令+管道 的方式. 在这种情况下, 有方法可以解决或规避此问题吗?
服务端的完整代码, 其中相关的代码如下:
let args = [ '-ss', startTimeStr, '-t', durationTimeStr, '-accurate_seek', '-i', videoPath, '-map', '0:v:0', '-c:v', encoder, '-b:v', String(bitrate), '-bsf:v', 'h264_mp4toannexb', '-avoid_negative_ts', 'make_zero', '-start_at_zero', '-muxdelay', delayTimeStr, '-muxpreload', delayTimeStr, '-f', 'mpegts', 'pipe:1' ]; 请求方法:
m3u: http://127.0.0.1:8082/video/rttPlaylist?videoPath=1.mkv
segment: http://127.0.0.1:8002/video/rttSegment?videoPath=1.mkv&start=0.0000&duration=4.0000
(BTW: 其实大部分是 Gemini 写的.)
1 lslqtz OP 第一个切片: PTS[0]*timeBase 至 PTS[1]*timeBase. 第二个切片: PTS[1]*timeBase 至 PTS[2]*timeBase. ... 最后一个切片: PTS[i]*timeBase 至 videoDuration. (segment 还有一个 audio 参数, 播放列表会自动分析, 单独请求切片不给出的话会默认为 0 即没有音频.) |
2 lslqtz OP 还有一个分支实现的是按时间切割分片的方案, 但也是有这样的问题, 而且因为非关键帧切割所以有时会因为搜寻目标时间帧耗费较多的时间及性能. |
3 wnpllrzodiac 332 天前 via Android nginx-vod 模块,mp4 分片在内存。你可以试试。应该比自己写靠谱。 |
4 wnpllrzodiac 332 天前 via Android 多媒体处理建议用 c 写 |
5 ethusdt 332 天前 via iPhone 让 ai 用 libav 重写一下,不要用命令行工具 |
6 edcopclub 332 天前 via Android 试试 mkv 的 cue |
7 lslqtz OP 谢谢楼上各位的建议, 我会去看看 nginx-vod 模块和 libav (不过复杂性会提升不少). 主要做出来就一个音频顿卡的话抛开重做还是有些...嗯. |
8 lslqtz OP nginx-vod 模块确实可以满足 mp4 的切片需求, 但是不适用于 mkv, 需要重新转容器. 打算晚些时候试试 libav 的方式, 就是实现复杂性提升不少, 即使使用 ai 也会有点复杂 (现在的 ai 还是有些傻). |
9 lslqtz OP 然后 mkv 的 cue 的话, 由于是在浏览器进行点播, 而 mkv 本身的支持就有些问题, 所以可能不太好. |