これはきちんとした問題であり、「どのコードが機能するかを教えてください」ではなく、「この状況を論理的にどのように処理するか」という質問です。
要するに、私はRTSPを介してIPカメラから入ってくるビデオ+オーディオを持っています。
ビデオとオーディオは、別々のスレッド(以下に表示)によって、フレームごとに単一のmp4コンテナにデコードおよび記録されています。
問題は、各ビデオフレームのTimeSpanの終了時間と開始時間の精度が不足しているため、時間の経過とともにビデオとオーディオの同期が徐々にずれていくということです。
各ビデオフレームの継続時間は1/フレームレート=0.0333667000333667である必要がありますが、(FromTicks()メソッドを使用しても)最初のフレームの開始時間= 0.0、終了時間0.0333667を使用しています。
ビデオデコーダーのフレームレート値を29.97から調整できます(フレームレートとして宣言されたカメラの設定から取得します)。その結果、ビデオがオーディオの前にあるか、オーディオの後に遅れます。これは、各ビデオのmediaBuffer.StartTimeとmediaBufferを作成するだけです。 .EndTimeは、オーディオと比較して早すぎるか遅すぎます。
時間の経過とともに、わずかな10進数の切り捨てにより、ビデオとオーディオの同期がとれなくなります。録音が長くなるほど、2つのトラックの同期がとれなくなります。
丸め誤差は論理的に重要ではないため、なぜこれが発生するのかはよくわかりません。
精度が1秒しかない場合でも、毎秒ビデオフレームを書き込むだけで、タイムラインでの配置はおおよそ+1秒になるはずです。これにより、すべてのプログレッシブフレームが同じになります+ -本来あるべき場所まで1秒で、徐々に置き忘れが増えることはありません。これは各フレームで次のようになると思います。
[<---------1秒-------->予想される正確なフレーム時間<--------+1s -------->] --- -------------------------------------------------記録フレームタイム - - - -
ここで何かが足りませんか?
「新しいフレーム開始時間=最後のフレーム終了時間、新しいフレーム終了時間=新しいフレーム開始時間+ 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++;
}
}