5

これはきちんとした問題であり、「どのコードが機能するかを教えてください」ではなく、「この状況を論理的にどのように処理するか」という質問です。

要するに、私は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++;
        }
    }
4

2 に答える 2

1

注意すべき点がいくつかあります。

  1. 不適切な手動フレームタイムスタンプ。通常、ドライバー/カード/その他のフレーム時間を指定するのではなく、手動でフレーム期間を計算することはお勧めできません。フレームを自分でスタンプすると、ビットレートの変動やコンピュータの内部タイミングなどが原因で、ほとんどの場合ドリフトが発生します。

  2. 精密ドリフト。ミリ秒単位のフレームタイムスタンプを処理するときにドリフトが発生しましたが、ソースタイムスタンプはナノ秒単位でした。これは私がダブルをロングにキャストすることを要求しました。

    たとえば、directshowからナノ秒単位のメディア時間を取得しますが、内部計算にはミリ秒単位が必要です。これは、nsとmsの間で変換する必要があることを意味します。私にとって、それは精度の損失があったところです。これに対する私の解決策は、精度の低下を追跡する必要があるということです。

    私が過去に行ったことは、実行中の「timingFraction」カウンターがあることです。基本的に、除算を行うときはいつでも、フレームの基本的なタイムスタンプ(つまり、フレーム時間/ NS_PS_MS)が得られます。ただし、事前にキャストされたタイムスタンプのドロップされた小数部分もタイミング小数カウンターに追加します(c ++ではmodf関数を使用しました)。ここで、タイミングフラクションが整数の場合、キャストされたタイムスタンプ(longにキャストされたため整数)と残りのタイミングフラクションを追加します。基本的に、余分なミリ秒を累積した場合は、必ずフレームに追加してください。このようにして、あらゆる精度のドリフトを補正できます。

  3. アコーディオン効果。 時間の経過とともにすべてが正しいものになる可能性があり、1秒の造粒でも一致するはずだと思いますが、一致しません。オーディオは完全に一致する必要があります。一致しないと、奇妙に聞こえます。これは通常、適切なタイミングで人から適切な音声が聞こえることを特徴としていますが、唇は一列に並んでいません。時間が経つにつれて、すべてはまだ問題ありませんが、何も完全に並んでいません。これは、適切なタイミングでフレームをレンダリングしていないためです。一部のフレームは少し長すぎ、一部のフレームは少し短すぎます。すべてが適切な場所になりますが、適切な長さはありません。

さて、精度がすでに100ナノ秒レベルにあるのに、なぜこれに遭遇するのか、おそらく項目1のように聞こえます。次に進む前に、右端のタイムスタンプを計算していることを確認します。

また、フレーム間のデルタを合計して、正しく追加されていることを確認するテストを実行することもあります。ストリームの継続時間中の各フレーム間の時間の合計は、ストリーミングされている時間と等しくなる必要があります。つまり、フレーム1の長さは33ミリ秒、フレーム2の長さは34ミリ秒で、67ミリ秒録音しました。70ミリ秒間録音した場合、どこかで何かを失いました。ドリフトは通常、数時間後に現れ、オーディオとビデオを一致させると耳/目で簡単に検出できます。

また、ハンスのコメントに対抗するために、オーディオエンジニアリングの世界はこれについて多くのことを言う必要があります。特にビデオフィードバックと組み合わせると、10msでレイテンシーを聞くことができます。10ミリ秒の遅延は表示されない場合がありますが、確実に聞こえます。http://helpx.adobe.com/audition/kb/troubleshoot-recording-playback-monitoring-audition.htmlから

待ち時間に適用される一般的なガイドライン

10ミリ秒未満-エフェクトを含む着信トラックのリアルタイムモニタリングを可能にします。

10ミリ秒で-レイテンシーを検出できますが、それでも自然に聞こえ、モニタリングに使用できます。

11〜20ミリ秒-モニタリングが使用できなくなり、実際の音源が不鮮明になり、>モニタリングされた出力が明らかになります。

20〜30ミリ秒-遅延音は、元の信号の成分ではなく、実際の遅延のように聞こえ始めます。

私はここでちょっと怒鳴りましたが、たくさんのことが起こっています。

于 2013-01-22T22:08:51.690 に答える
1

際立っていることの 1 つは、フレームレートの計算が間違っていることです。

各ビデオ フレームの持続時間は 1 / フレームレート = 0.0333667000333667 である必要があります。

29.97それがフレームレートとして使用するときです。29.97 は単なる表示値です。実際のフレームレートは30 / 1.001 = 29.97002997002997FPS です。したがって、1 フレームは1 / (30 / 1.001) = 0.0333666666666667数秒続きます。ソース、「60i」を参照。

于 2013-01-31T22:18:22.867 に答える