2010-12-01 33 views
0

我正在使用C#(.NET 4.0)從WPF項目中捕獲需要保存到磁盤(BMP格式)的高速攝像頭的300幀視頻幀序列。視頻幀需要以接近精確的時間間隔進行捕獲,因此我無法在幀捕獲時將幀保存到磁盤 - 磁盤I/O不可預知,並且會丟棄幀之間的時間間隔。採集卡有大約60個幀緩衝區可用。Semaphore是這個視頻序列捕獲/保存工作的正確工具嗎?

我不確定實施此問題的解決方案的最佳方法是什麼。我最初的想法是創建一個「BufferToDisk」線程,在幀緩衝區變爲可用時保存圖像。在這種情況下,主線程捕獲一個幀緩衝區,然後發信號通知線程指示可以保存幀。問題在於幀被捕獲的速度比線程可以保存文件的速度快,因此需要進行某種同步處理。我在想信號量會是這個工作的好工具。不過,我從來沒有用這種方式使用信號量,所以我不知道如何繼續。

這是一個合理的方法來解決這個問題嗎?如果是這樣,有人可以發佈一些代碼讓我開始?

任何幫助,非常感謝。

編輯: 在查看鏈接的「C#中的線程 - 第2部分」書籍摘錄後,我決定通過調整「ProducerConsumerQueue」類示例來實現解決方案。這裏是我的適應代碼:

class ProducerConsumerQueue : IDisposable 
{ 
    EventWaitHandle _wh = new AutoResetEvent(false); 
    Thread _worker; 
    readonly object _locker = new object(); 
    Queue<string> _tasks = new Queue<string>(); 

    public ProducerConsumerQueue() 
    { 
     _worker = new Thread(Work); 
     _worker.Start(); 
    } 

    public void EnqueueTask(string task) 
    { 
     lock (_locker) _tasks.Enqueue(task); 
     _wh.Set(); 
    } 

    public void Dispose() 
    { 
     EnqueueTask(null);  // Signal the consumer to exit. 
     _worker.Join();   // Wait for the consumer's thread to finish. 
     _wh.Close();   // Release any OS resources. 
    } 

    void Work() 
    { 
     while (true) 
     { 
      string task = null; 
      lock (_locker) 
       if (_tasks.Count > 0) 
       { 
        task = _tasks.Dequeue(); 
        if (task == null) 
        { 
         return; 
        } 
       } 
      if (task != null) 
      { 
       // parse the parameters from the input queue item 
       string[] indexVals = task.Split(','); 
       int frameNum = Convert.ToInt32(indexVals[0]); 
       int fileNum = Convert.ToInt32(indexVals[1]); 
       string path = indexVals[2]; 
       // build the file name 
       string newFileName = String.Format("img{0:d3}.bmp", fileNum); 
       string fqfn = System.IO.Path.Combine(path, newFileName); 
       // save the captured image to disk 
       int ret = pxd_saveBmp(1, fqfn, frameNum, 0, 0, -1, -1, 0, 0); 
      } 
      else 
      { 
       _wh.WaitOne();   // No more tasks - wait for a signal 
      } 
     } 
    } 
} 

在主程序使用類:

// capture bitmap images and save them to disk 
using (ProducerConsumerQueue q = new ProducerConsumerQueue()) 
{ 
    for (int i = 0; i < 300; i++) 
    { 
     if (curFrmBuf > numFrmBufs) 
     { 
       curFrmBuf = 1; // wrap around to the first frame buffer 
     } 

     // snap an image to the image buffer 
     int ret = pxd_doSnap(1, curFrmBuf, 0); 

     // build the parameters for saving the frame to image file (for the queue) 
     string fileSaveParams = curFrmBuf + "," + (i + 1) + "," + newPath; 
     q.EnqueueTask(fileSaveParams); 

     curFrmBuf++; 
    } 
} 

非常漂亮的類 - 此功能有少量的代碼。

非常感謝您的建議,夥計們。

回答

1

當然,聽起來很合理。您可以使用信號量或其他線程同步原語。這聽起來像一個標準的生產者/消費者問題。對於一些僞代碼 ,請看here

+0

謝謝 - 它確實看起來像生產者/消費者問題。我會仔細研究一下,看看我能否基於此制定解決方案。 – PIntag 2010-12-01 21:38:56

1

如果磁盤速度太慢(例如某些其他進程掛鉤它)60幀緩衝區不足,會發生什麼情況?也許你需要一個BufferToMemoryBufferToDisk線程或某種組合。您需要主線程(捕獲到緩衝區)具有最高優先級,BufferToMemory介質和BufferToDisk最低。

無論如何,回到信號量,我推薦你閱讀這個:http://www.albahari.com/threading/part2.aspx#_Semaphore。信號量應該爲你做,但我會推薦SemaphoreSlim(.NET 4)。

+0

是的,慢速磁盤I/O是一個值得關注的問題,但這是一個專用系統,我們的基準測試表明,這種情況不太可能發生。 – PIntag 2010-12-01 21:32:06

+0

哦,順便說一句,謝謝你的鏈接,尼爾森。我使用ProducerConsumerQueue來實現該鏈接的解決方案,並且它工作得非常好。我意識到,如果卡的視頻緩衝區在文件保存之前過得太遠,可能會出現問題,但這在我的應用程序環境中應該不成問題。噢,捕獲卡有75個緩衝區,而不是我原先想象的60個緩衝區,所以應該沒問題。 – PIntag 2010-12-02 16:46:29

1

由於您將此視爲生產者/消費者問題(根據您對@ siz答案的回覆來判斷),您可能需要查看BlockingCollection<T>,這是專門爲此類情景設計的。

它允許任意數量的生產者線程將數據推送到集合中,並允許任意數量的消費者線程再次將其拉出。在這種情況下,您可能只需要一個生產者和一個消費者線程。

BlockingCollection<T>做了所有的工作,確保消費者線程只有在生產線程表示需要完成更多工作時纔會喚醒和處理工作。而且它還考慮到允許建立一個工作隊列。

相關問題