Qtオーディオおよびビデオ開発24-ffmpegオーディオおよびビデオ同期

I.はじめに

私はオーディオとビデオの同期にffmpegを使用しています。個人的には、これがffmpegの基本処理で最も難しいものだと思います。無数の人々がここで立ち往生しています。方法はありません。インターネットでさまざまなデモを試しましたが、基本的にはスカムです。非常に少数のビデオファイルのみをサポートします。たとえば、受信したデータパケットは1フレームのビデオと1フレームのオーディオです。まったく同期できないか、進行状況が飛び越えて直接クラッシュします。実際、最も完璧なオーディオとビデオ同期デモはffplayです。私は数十のさまざまなオーディオおよびビデオローカルファイル、数十のビデオストリームファイルを個人的にテストしましたが、それらはすべて完璧です。もちろん、これは私自身のものであり、完璧ではありません。

ビデオストリームのみ(オーディオストリームなし)を再生する場合は、オーディオとビデオを同期する必要がない場合があるため、最初にrtspビデオストリームのみを再生する場合は、同期の問題は発生しなかったため、まったく考慮していませんでした。 rtmp、http、m3u8などのビデオストリームを植えたとき、問題はさらに大きくなりました。一度にhls形式のビデオストリームファイルから発生し、小さなビデオファイルが発生しました。同期されていない場合、突然過去にスワイプされた写真はたくさんありますが、次に来るときに再びスワイプされます。そのため、自分で同期を計算する必要があります。前回受信したデータパケットはキューに入れられ、表示する必要があるときに表示されます。

一般的なオーディオとビデオの同期方法:

  1. fpsによって制御され、fpsは25フレームなど、1秒間に再生されるフレーム数を示します。1フレームをデコードするために使用される時間は自分で計算できます。1フレームが占有します(1000/25 = 40ミリ秒)。これは遅延によって処理されます。これは実際には最もスカムバッグの方法。
  2. デコードを開始する時間startTimeを思い出し、av_rescale_qを介してpts時間を計算します。2つの時間の差は、遅延する必要がある時間です。遅延するには、av_usleepを呼び出します。ファイルの一部のみが正常ですが、多くの場合正常ではありません。
  3. オーディオはビデオに同期され、ビデオクロックはマスタークロックとして使用されますが、まだ使用していません。インターネット上の多くの人がこの方法は適切ではないと言っています。
  4. 映像は音声に同期していて、マスタークロックは音声クロックを使っているので、まだ試していませんが、ほとんどの人が使っているそうです。
  5. オーディオとビデオは外部クロックに同期され、外部クロックがマスタークロックとして使用されます。最後の方法は理解しやすく、相互に干渉せず、それぞれが外部クロックに従って同期します。
  6. ffplayには、パラメーターで制御できる3つの同期戦略が組み込まれています。デフォルトでは、ビデオをオーディオに同期します。

2.機能

  1. ビデオストリーム+ローカルビデオ+ USBカメラなどのマルチスレッドリアルタイム再生
  2. windows + linux + macをサポートし、ffmpeg3およびffmpeg4をサポートし、32ビットおよび64ビットをサポートします。
  3. メインインターフェイスにスタックされていないマルチスレッドディスプレイイメージ。
  4. Webカメラを自動的に再接続します。
  5. 境界線のサイズ、オフセット、境界線の色を設定できます。
  6. OSDラベルを描画するかどうか、つまりラベルテキストまたは画像とラベル位置を描画するかどうかを設定できます。
  7. 2つのOSD位置とスタイルを設定できます。
  8. ファイルに保存するかどうか、ファイル名を設定できます。
  9. ファイルを直接ffmpegwidgetコントロールにドラッグして再生できます。
  10. h265ビデオストリーム+ rtmpなどの一般的なビデオストリームをサポートします。
  11. 再生を一時停止して再開できます。
  12. 単一のビデオファイルのストレージとビデオファイルのタイミングストレージをサポートします。
  13. 上部のフローティングバーをカスタマイズし、クリック信号通知を送信して、それを有効にするかどうかを設定します。
  14. 画面のストレッチ塗りつぶしまたは同じ比率の塗りつぶしを設定できます。
  15. デコードを速度優先、品質優先、およびイコライゼーション処理に設定できます。
  16. スクリーンショット(元の写真)とビデオのスクリーンショットを撮ることができます。
  17. ビデオファイルストレージは、ベアストリームとMP4ファイルをサポートします。
  18. オーディオとビデオは、外部クロック同期戦略を使用して完全に同期されます。
  19. 再生位置の検索をサポートします。
  20. qsv、dxva2、d3d11vaなどのハードデコードをサポートします。
  21. OpenGLをサポートしてビデオデータを描画します。CPU使用率は非常に低くなります。
  22. Androidと組み込みLinuxをサポートし、クロスコンパイルするだけです。

3、レンダリング

ここに画像の説明を挿入

4、関連サイト

  1. 国内サイト:https : //gitee.com/feiyangqingyun/QWidgetDemo
  2. 国際サイト:https : //github.com/feiyangqingyun/QWidgetDemo
  3. 個人ホームページ:https : //blog.csdn.net/feiyangqingyun
  4. Zhihuホームページ:https ://www.zhihu.com/people/feiyangqingyun/
  5. エクスペリエンスアドレス:https : //blog.csdn.net/feiyangqingyun/article/details/97565652

5、コアコード

void FFmpegSync::run()
{
    reset();
    while (!stopped) {
        //暂停状态或者队列中没有帧则不处理
        if (!thread->isPause && packets.count() > 0) {
            mutex.lock();
            AVPacket *packet = packets.first();
            mutex.unlock();

            //h264的裸流文件同步有问题,获取不到pts和dts,暂时用最蠢的办法延时解决
            if (thread->formatName == "h264") {
                int sleepTime = (1000 / thread->videoFps) - 5;
                msleep(sleepTime);
            }

            //计算当前帧显示时间 外部时钟同步
            ptsTime = getPtsTime(thread->formatCtx, packet);
            if (!this->checkPtsTime()) {
                msleep(1);
                continue;
            }

            //显示当前的播放进度
            checkShowTime();

            //0-表示音频 1-表示视频
            if (type == 0) {
                thread->decodeAudio(packet);
            } else if (type == 1) {
                thread->decodeVideo(packet);
            }

            //释放资源并移除
            thread->free(packet);
            mutex.lock();
            packets.removeFirst();
            mutex.unlock();
        }

        msleep(1);
    }

    clear();
    stopped = false;
}

bool FFmpegSync::checkPtsTime()
{
    bool ok = false;
    if (ptsTime > 0) {
        if (ptsTime > offsetTime + 100000) {
            bufferTime = ptsTime - offsetTime + 100000;
        }

        int offset = (type == 0 ? 1000 : 5000);
        offsetTime = av_gettime() - startTime + bufferTime;
        if ((offsetTime <= ptsTime && ptsTime - offsetTime <= offset) || (offsetTime > ptsTime)) {
            ok = true;
        }
    } else {
        ok = true;
    }

    return ok;
}

おすすめ

転載: blog.csdn.net/feiyangqingyun/article/details/108770121