我正在尝试编写将传入的视频帧保存到磁盘的 C++ 代码。异步到达的帧由生产者线程推送到队列中。消费者线程将帧从队列中弹出。生产者和消费者的相互排斥是使用互斥体完成的。但是,我仍然注意到帧被丢弃。丢弃的帧(可能)对应于生产者尝试将当前帧推入队列但由于消费者持有锁而无法这样做的实例。有什么建议么 ?我基本上不希望制片人等待。等待消费者对我来说没问题。
EDIT-0:不涉及锁定的替代想法。这行得通吗?
- Producer 最初将
n
几秒钟的视频排入队列。n
可以是帧速率的一些小的倍数。 - 只要队列包含
>= n
几秒钟的视频,消费者就会逐帧出列并保存到磁盘。 - 视频完成后,队列被刷新到磁盘。
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 的 DDJ 文章不是一个选择。
(2) 我找到了一个无界的单一生产者-消费者队列的引用。但是,作者指出排队(添加帧)可能不是免等待的。
(3) 我还找到了liblfds,一个 C 库,但不确定它是否能达到我的目的。