头图:こんきと老师 https://twitter.com/_konkito
开篇严谨一点,本文并不能解决全部情况,还是要具体情况具体分析,只是B站录像出问题最多的都是时间戳的问题,修复方式对一般人来说相对难以接触,碰巧昨天两个组激光都遇到了问题,斗胆尝试普及一下我的修复方法。
*并且如果不急的话现在官号录像回放修好了,等官号就行了,只有等级不够与激光抢时间时需要
当你打开录像文件夹,不管使用的是什么windows带GUI的一键录制软件,还是linux命令行的biliroku-lite,一般来说你会发现B站的录像在处理前会是flv文件。MP4仍然是现在最常见的格式,通常来说在使用之前最好经过封装转成MP4(如果你拿到的扔进pr或者aeg的文件是flv甚至ts请找你们家源man算账)
在封装的时候你们家源man发现……
ffmpeg -i xxxx.flv -c copy xxxx.mp4
时间戳炸了
(但是如果只发生在结尾那并没有任何所谓,阿B直播结束后鬼畜回放是正常情况,简单剪掉即可)
如果你家的源man不会ffmpeg,那么他并不知道上面的图是什么,不过没关系,他在看完这篇之后就会ffmpeg对源man最常用的功能了
时间戳炸了在播放视频时的表现包括
-
我家V播了一个多小时但文件时长只有几十分钟?!(但是按码率粗略判断文件大小是正常的)
-
不跳进度条播放到某个时间点处时,进度条不动了,但仍有内容播放(有可能是画面卡住只有音频),拖进度条过这个时间点后感觉跳跃了很多时间,即仿佛在这个没有长度的时间点处藏着几分钟的视频一样
-
进度条时间按正常速度增长,但画面明显跳进
总之,就是 感觉我的视频文件应该包含了全部的媒体信息,但我无法播放
那么一般来说,这就是阿B的流时间戳炸了。本文尝试普及修复这一问题的方式,使用工具为ffmpeg,会顺带提一点必要的视频编码知识和ffmpeg的常用功能
时间戳(timestamp)和出问题的原因
我不是什么专业人员所以术语可能不准确以及概念解释并不会很严谨,但应该是至少大部分情况下拿来理解是没什么问题的,请不要鲨我(发抖)
时间戳是流的时间信息,MP4文件可以拖进度条,是因为它对视频流和音频流打上了时间戳,通过制定跳进的时间就可以跳到流的相应位置,类比的话可以想象成尺子(而另一种常见录像格式TS就没有时间戳)(也存在特殊的MP4文件没有时间戳而无法跳进)
而时间戳坏了,相当于我的尺子刻度并不是线性增长的,有的部分没有刻度,甚至刻度会重复两遍,所以在检索的时候就无法检索到这些坏了的部分
那么如何去找到这些丢失的部分?只要无视刻度从头到尾过一遍就可以了,然后重新画上刻度,就能修复成完好的视频文件。
如何修复
修复要用的软件为FFmpeg,是一个多平台的命令行工具(CLI)。最简单的,下载static build就行了,解压后如应该是三个exe文件。(如果你用linux那应该不需要看我这里说的任何废话)
https://ffmpeg.zeranoe.com/builds/
把这个文件夹添加到windows环境变量,这样就可以在powershell或cmd里无需路径直接使用命令ffmpeg(如果实在不会,那把视频文件扔到这个文件夹下并在此处打开powershell或cmd也可以用./ffmpeg来调用)
https://superuser.com/questions/1159056/ffmpeg-recreate-timestamps-without-reencoding
修复的方法来自以上链接,第一步是把文件分离成音频和视频流。这里我无法给一个严格的解释,不过两者会互相影响时间戳所以可以理解吧。视频流的格式是h264,而音频一般在非超长耐久档时,选择wav应该是最好的。印象中wav在6G?左右会达到上限,因此超长耐久无法使用wav(比如某次CP的一整天夏色吹雪档),这时我会选择flac。
ffmpeg -i 源文件.mp4 -map 0:v -vcodec copy -bsf:v h264_mp4toannexb video.h264
-i后接输入文件的文件名,map指定映射(我觉得似乎可以去掉,没尝试过,保留也只是多打几个字),这里选择的是源文件里的视频流部分,-vcodec copy指复制视频流,-bsf部分留着即可,之后是输出的文件名,我习惯以video.h264命名
ffmpeg -i 源文件.mp4 -vn audio.wav
这行用来抽取音频,-vn表示无视频流
之后对音视频文件重写时间戳
ffmpeg -fflags +genpts -r 60 -i source-video.h264 -vcodec copy video.mp4
-fflags +genpts是重新创建时间戳的指令,-r为指定帧率,因为流是一帧一帧编码,如果60帧的流按30帧编,播放速度会变为一半,请务必把60替换为正确的fps。同样,复制视频流,我们不想产生任何画质损失,也不想花费大把的时间进行重编码。这步的输出为video.mp4
而音频的重编码是无可奈何,因为-acodec copy保存成m4a是一定会失败的。这一步的指令为
ffmpeg -i audio.wav -ab 160k audio.m4a
这里把wav文件重编码为m4a格式用于之后mux成mp4文件,-ab指定音频的码率,知道流的码率最好(比如通过以前没有问题的录像得到的信息,同一个V一般流的码率会保持一致),不知道的话160k比较保险,又不会体积太大,也不会损害音质(320k非常少见)
最后把修复好的音频和视频合在一起(Mux)
ffmpeg -i video.mp4 -i audio.m4a -c copy output_fixed.mp4
复制音频和视频流合成一个输出文件。
但是有时候会有音画不同步的现象,原因未知,如果音频落后可估计时间差然后对流稍作剪辑对轨
ffmpeg -i audio.m4a -ss 落后时间 -to 停止时间 -c copy audio_shift.m4a
落后时间的单位为秒,或者以hh:mm:ss格式指定,停止时间同样,如果你的流以鬼畜结尾并且不想保留,那么可以在鬼畜开始前结束。
如果是视频落后,那就比较麻烦了,推荐扔进PR进行对轨。(因为FFmpeg对I帧的操作说实话有些迷,有时aeg会出轴错位有时不会,没有精力去彻底研究)
另外一种最麻烦的情况,是同时存在流的信息不完整,比如有几秒我的视频流丢了,这种情况在重新写时间戳的时候因为毫无音频作为参考,这段时间就彻底被抹去了。这种时候我只见过一次,没有想到什么好办法,似乎只能先做一遍上面的步骤然后扔进PR找断点逐段修复
附常用ffmpeg指令
封装(复制音视频流,改变容器格式)
ffmpeg -i input -c copy output
抽取音频流
ffmpeg -i input -vn -acodec copy audio_output
拼接
首先新建一个文本文档比如cat.txt,输入
file 文件1
file 文件2
……
保存,然后使用如下指令
ffmpeg -f concat -i cat.txt -c copy output