1

着信ビデオフレームをディスクに保存するC++コードを書き込もうとしています。非同期に到着するフレームは、プロデューサースレッドによってキューにプッシュされます。フレームは、コンシューマスレッドによってキューからポップされます。プロデューサーとコンシューマーの相互排除は、ミューテックスを使用して行われます。ただし、フレームがドロップされていることに気付きます。ドロップされたフレームは(おそらく)プロデューサーが現在のフレームをキューにプッシュしようとしたが、コンシューマーがロックを保持しているためにプッシュできない場合に対応します。助言がありますか ?私は本質的にプロデューサーを待たせたくありません。待っている消費者は私にとって大丈夫です。

EDIT-0:ロックを伴わない代替案。これは機能しますか?

  1. プロデューサーは最初にn数秒分のビデオをキューに入れます。nフレームレートの小さな倍数にすることができます。
  2. キューに>= n数秒分のビデオが含まれている限り、コンシューマーはフレームごとにデキューし、ディスクに保存します。
  3. ビデオが完了すると、キューはディスクにフラッシュされます。

編集-1:フレームは約15fpsで到着します。

編集-2:コードの概要:

メインドライバーコード

 // Main function
 void LVD::DumpFrame(const IplImage *frame)
 {

     // Copies frame into internal buffer.
     // buffer object is a wrapper around OpenCV's IplImage
     Initialize(frame);

     // (Producer thread) -- Pushes buffer onto queue 
     // Thread locks queue, pushes buffer onto queue, unlocks queue and dies
     PushBufferOntoQueue();

     // (Consumer thread) -- Pop off queue and save to disk
     // Thread locks queue, pops it, unlocks queue,
     // saves popped buffer to disk and dies                
     DumpQueue();

     ++m_frame_id;
}

void LVD::Initialize(const IplImage *frame)
{

    if(NULL == m_buffer) // first iteration 
         m_buffer = new ImageBuffer(frame);         
    else    
         m_buffer->Copy(frame); 
}

プロデューサー

void LVD::PushBufferOntoQueue()
{   
     m_queingThread = ::CreateThread( NULL, 0, ThreadFuncPushImageBufferOntoQueue, this, 0, &m_dwThreadID);
}

 DWORD WINAPI LVD::ThreadFuncPushImageBufferOntoQueue(void *arg)
 {

     LVD* videoDumper = reinterpret_cast<LVD*>(arg);
     LocalLock ll( &videoDumper->m_que_lock, 60*1000 ); 
     videoDumper->m_frameQue.push(*(videoDumper->m_buffer));
     ll.Unlock();   
     return 0;
 }

消費者

void LVD::DumpQueue()
{   
    m_dumpingThread = ::CreateThread( NULL, 0, ThreadFuncDumpFrames, this, 0, &m_dwThreadID);       
}

 DWORD WINAPI LVD::ThreadFuncDumpFrames(void *arg)
 {
        LVD* videoDumper = reinterpret_cast<LVD*>(arg);

        LocalLock ll( &videoDumper->m_que_lock, 60*1000 );
        if(videoDumper->m_frameQue.size() > 0 )
        {
           videoDumper->m_save_frame=videoDumper->m_frameQue.front();
           videoDumper->m_frameQue.pop();
        }
        ll.Unlock();    

    stringstream ss;
    ss << videoDumper->m_saveDir.c_str() << "\\";
    ss << videoDumper->m_startTime.c_str() << "\\";     
    ss << setfill('0') << setw(6) << videoDumper->m_frame_id;
    ss << ".png";       
    videoDumper->m_save_frame.SaveImage(ss.str().c_str());

    return 0;

}

ノート:

(1)C++11が使えません。したがって、ハーブサッターのDDJ記事はオプションではありません。

(2)無制限の単一の生産者/消費者キューへの参照を見つけました。ただし、作成者は、エンキュー(フレームの追加)はおそらく待機なしではないと述べています。

(3) Cライブラリであるliblfdsも見つけましたが、それが私の目的に役立つかどうかはわかりません。

4

2 に答える 2

4

キューが問題になることはありません。ビデオフレームは、最悪の場合、16ミリ秒間隔で到着します。キューに保存する必要があるのは、フレームへのポインタだけです。スレッドセーフな方法で1つを追加/削除すると、1マイクロ秒以上かかることはありません。

別の説明と解決策を探す必要があります。ビデオは永遠に消防ホースの問題を提示します。ディスクドライブは通常、非圧縮のビデオストリームに追いつくのに十分な速度ではありません。したがって、消費者がプロデューサーに追いつけない場合は、何かが与えられます。ドロップされたフレームでは、キューが無制限に大きくなるのを(正しく)防止した場合に発生する可能性があります。

ビデオのエンコードを必ず検討してください。リアルタイムMPEGおよびAVCエンコーダーが利用可能です。彼らがストリームを圧縮した後、あなたはディスクに追いつくのに問題がないはずです。

于 2013-01-23T00:08:43.347 に答える
0

循環バッファは間違いなく良い代替手段です。2 ^ nサイズを使用する場合は、このトリックを使用してポインターを更新することもできます。

inline int update_index(int x) 
{
   return (x + 1) & (size-1);
}

このように、高価な比較(および結果として生じるジャンプ)または除算(任意のプロセッサで最も高価な整数演算-「メモリの大きなチャンクの充填/コピー」タイプの演算をカウントしない)を使用する必要はありません。

ビデオ(または一般的なグラフィックス)を扱うときは、「バッファ管理」を行うことが不可欠です。通常、これは「フレームバッファ」の状態を追跡し、必要以上にコンテンツをコピーしないようにする場合です。

典型的なアプローチは、2つまたは3つのビデオバッファ(またはフレームバッファ、またはあなたがそれを呼ぶもの)を割り当てることです。バッファーは、プロデューサーまたはコンシューマーのいずれかが所有できます。譲渡は所有権のみです。したがって、ビデオドライバーが「このバッファーがいっぱいです」と通知すると、所有権はコンシューマーにあり、コンシューマーはバッファーを読み取り、ディスク[またはその他]に保存します。保存が終了すると、プロデューサーが再利用できるように、バッファーが戻されます(「解放」されます)。バッファからデータをコピーするのはコストがかかる[時間がかかる]ので、絶対に必要でない限り、それを実行したくありません。

于 2013-01-22T23:29:52.087 に答える