【发布时间】:2013-01-04 17:35:59
【问题描述】:
这是一个简洁的问题,不是“告诉我什么代码有效”,而是“我如何在逻辑上处理这种情况”的问题。
简而言之,我有来自 IP 摄像机通过 RTSP 输入的视频 + 音频。
视频和音频通过单独的线程逐帧解码并记录到单个 mp4 容器中(如下所示)。
由于每个视频帧的 TimeSpan 结束时间和开始时间不够精确,问题是随着时间的推移,视频和音频变得越来越不同步。
每个视频帧的持续时间应该是 1 / framerate = 0.0333667000333667,但它使用(即使使用 FromTicks() 方法),第一帧的开始时间 = 0.0 和结束时间 0.0333667。
我可以从 29.97 调整视频解码器的帧速率值(它是从相机的设置声明的帧速率中提取的),从而导致视频先于音频,或者滞后于音频——这只是使每个视频 mediaBuffer。与音频相比,StartTime 和 mediaBuffer.EndTime 太早或太晚。
随着时间的推移,微小的小数截断最终会导致视频和音频不同步 - 录制时间越长,两个轨道越不同步。
我真的不明白为什么会发生这种情况,因为从逻辑上讲,舍入误差应该无关紧要。
即使我只有 1 秒的精度,我也只会每秒写一个视频帧,它在时间轴中的位置大致应该是 +- 1 秒,这应该使每个渐进帧相同的 +- 1 秒到它应该在的位置,而不是逐渐增加更多的错位。我想象这对于每一帧来说都是这样的:
[ 预期的确切帧时间 ] -------------------------------------------------- -- 记录帧时间--------
我错过了什么吗?
我不是在做“新帧开始时间 = 上一帧结束时间,新帧结束时间 = 新帧开始时间 + 1 / 帧率”——我实际上是在做“新帧开始时间 = 帧索引 - 1 /帧率,新帧结束时间=帧索引/帧率”。
也就是说,我正在根据它们应该具有的预期时间来计算帧开始和结束时间(帧时间 = 帧位置/帧速率)。
我的代码是这样的:
预计时间 ---------- 预计时间 ---------- 预计时间 框架时间框架时间框架时间
我从数学上理解这个问题,我只是不明白为什么小数截断会证明这样一个问题,或者从逻辑上知道解决它的最佳解决方案是什么。
如果我实现“每 x 帧,使用“(1 / 帧率)+ 一些数量”来弥补所有缺失的时间,是否有可能让帧匹配到它们应该在的位置,或者只是结果在凌乱的视频中?
public void AudioDecoderThreadProc()
{
TimeSpan current = TimeSpan.FromSeconds(0.0);
while (IsRunning)
{
RTPFrame nextFrame = jitter.FindCompleteFrame();
if (nextFrame == null)
{
System.Threading.Thread.Sleep(20);
continue;
}
while (nextFrame.PacketCount > 0 && IsRunning)
{
RTPPacket p = nextFrame.GetNextPacket();
if (sub.ti.MediaCapability.Codec == Codec.G711A || sub.ti.MediaCapability.Codec == Codec.G711U)
{
MediaBuffer<byte> mediaBuffer = new MediaBuffer<byte>(p.DataPointer, 0, (int)p.DataSize);
mediaBuffer.StartTime = current;
mediaBuffer.EndTime = current.Add(TimeSpan.FromSeconds((p.DataSize) / (double)audioDecoder.SampleRate));
current = mediaBuffer.EndTime;
if (SaveToFile == true)
{
WriteMp4Data(mediaBuffer);
}
}
}
}
}
public void VideoDecoderThreadProc()
{
byte[] totalFrame = null;
TimeSpan current = TimeSpan.FromSeconds(0.0);
TimeSpan videoFrame = TimeSpan.FromTicks(3336670);
long frameIndex = 1;
while (IsRunning)
{
if (completedFrames.Count > 50)
{
System.Threading.Thread.Sleep(20);
continue;
}
RTPFrame nextFrame = jitter.FindCompleteFrame();
if (nextFrame == null)
{
System.Threading.Thread.Sleep(20);
continue;
}
if (nextFrame.HasSequenceGaps == true)
{
continue;
}
totalFrame = new byte[nextFrame.TotalPayloadSize * 2];
int offset = 0;
while (nextFrame.PacketCount > 0)
{
byte[] fragFrame = nextFrame.GetAssembledFrame();
if (fragFrame != null)
{
fragFrame.CopyTo(totalFrame, offset);
offset += fragFrame.Length;
}
}
MediaBuffer<byte> mediaBuffer = new MediaBuffer<byte>(
totalFrame,
0,
offset,
TimeSpan.FromTicks(Convert.ToInt64((frameIndex - 1) / mp4TrackInfo.Video.Framerate * 10000000)),
TimeSpan.FromTicks(Convert.ToInt64(frameIndex / mp4TrackInfo.Video.Framerate * 10000000)));
if (SaveToFile == true)
{
WriteMp4Data(mediaBuffer);
}
lock (completedFrames)
{
completedFrames.Add(mediaBuffer);
}
frameIndex++;
}
}
【问题讨论】:
-
TimeSpan 的分辨率为 100 纳秒。因此,如果它一直关闭,那么一小时后您将无法关闭超过 100 纳秒 * 29.97 * 3600 = 11 毫秒。你看不到那个。你需要继续寻找。不相信实际的相机帧速率。并注意音频的可变比特率,这很常见。
-
嗯,肯定是帧率有问题,但如何才能真正解决呢?猜测?我可以让它接近、慢得多或快得多。但是,如果我没有办法实际计算写入该帧的正确时间是什么时候......有人甚至如何正确地进行 A/V 同步?
标签: c# .net video media recording