c++ - 将帧保存到磁盘时防止丢帧

标签 c++ multithreading video-streaming message-queue

我正在尝试编写将传入视频帧保存到磁盘的 C++ 代码。 异步 到达的帧由生产者线程推送到队列中。这些帧由消费者线程从队列中弹出。生产者和消费者的互斥是使用互斥体完成的。但是,我仍然注意到帧被丢弃。丢弃的帧(可能)对应于生产者试图将当前帧插入队列但由于消费者持有锁而无法这样做的情况。有什么建议 ?我基本上不希望制作人等待。等待的消费者对我来说没问题。

EDIT-0:不涉及锁定的替代想法。这行得通吗?

  1. Producer 最初将 n 秒的视频排队。 n 可以是帧速率的一些小倍数。
  2. 只要队列包含 >= n 秒的视频,消费者就会逐帧出队并保存到磁盘。
  3. 视频播放完毕后,队列会刷新到磁盘。

EDIT-1:帧以大约 15 fps 的速度到达。

EDIT-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。因此,Herb Sutter's DDJ article不是一个选项。

(2) 我找到了一个 reference到一个无界的单一生产者-消费者队列。但是,作者声明入队(添加帧)可能不是免等待的。

(3) 我还找到了liblfds ,一个 C 库,但不确定它是否符合我的目的。

最佳答案

队列不是问题。在最坏的情况下,视频帧以 16 毫秒的间隔到达。您的队列只需要存储一个指向帧的指针。以线程安全的方式添加/删除一个永远不会超过一微秒。

您需要寻找其他解释和解决方案。视频确实永远存在消防水带问题。磁盘驱动器的速度通常不足以跟上未压缩的视频流。因此,如果您的消费者无法跟上生产者的步伐,那么就会有一些事情发生。当您(正确地)阻止队列无限制地增长时,可能会出现丢帧的情况。

请务必考虑对视频进行编码。提供实时 MPEG 和 AVC 编码器。在他们压缩流后,您应该没有问题跟上磁盘。

关于c++ - 将帧保存到磁盘时防止丢帧,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14469799/

相关文章:

c++ - 在Visual Studio Code C++调试器中设置断点时出错

c++ - 具有单个互斥体的 std::scoped_lock 行为

Python - BaseHTTPServer.HTTPServer 并发和线程

java - java读取大文件时做 Action

video-streaming - 实时网络摄像头流 --> 服务器 --> 远程查看器

c++ - 限制通过c++中的套接字传输的数据

c++ - 为 bool 类型定义名称代替 "true"和 "false"

c++ - 如何在 C++ 中更改 freeglut 主窗口图标?

c++ - std::memory_order_relaxed 是否足以检查 "Availability"?

java - GUI 应用程序中线程的最佳方法(Java、swing)