所以我有一个提升的队列类,可以帮助多线程描述here。
在我的班级声明中我有
//...
struct VideoSample
{
const unsigned char * buffer;
int len;
};
ConcurrentQueue<VideoSample * > VideoSamples;
//...
struct AudioSample
{
const unsigned char * buffer;
int len;
};
ConcurrentQueue<AudioSample * > AudioSamples;
//...
在我班上我有一个功能:
void VideoEncoder::AddFrameToQueue(const unsigned char *buf, int size )
{
VideoSample * newVideoSample = new VideoSample;
VideoSamples.try_pop(newVideoSample);
newVideoSample->buffer = buf;
newVideoSample->len = size;
VideoSamples.push(newVideoSample);
//free(newVideoSample->buffer);
//delete newVideoSample;
}
我的应用程序只需要在队列中保留一个帧。
提供here关于如何删除结构的答案在这种情况下没有帮助,因为app粉碎了。我有类似的音频队列代码。
void VideoEncoder::AddSampleToQueue(const unsigned char *buf, int size )
{
AudioSample * newAudioSample = new AudioSample;
newAudioSample->buffer = buf;
newAudioSample->len = size;
AudioSamples.push(newAudioSample);
url_write (url_context, (unsigned char *)newAudioSample->buffer, newAudioSample->len);
AudioSamples.wait_and_pop(newAudioSample);
//delete newAudioSample;
}
和一个在单独的线程中运行的函数:
void VideoEncoder::UrlWriteData()
{
while(1){
switch (AudioSamples.empty()){
case true :
switch(VideoSamples.empty()){
case true : Sleep(5); break;
case false :
VideoSample * newVideoSample = new VideoSample;
VideoSamples.wait_and_pop(newVideoSample);
url_write (url_context, (unsigned char *)newVideoSample->buffer, newVideoSample->len);
break;
} break;
case false : Sleep(3); break;
}
}
}
顺便说一句:要将媒体数据流式传输到网址,我使用了ffmpeg的功能。
BTW:这里我用于队列的代码:
#include <string>
#include <queue>
#include <iostream>
// Boost
#include <boost/thread.hpp>
#include <boost/timer.hpp>
template<typename Data>
class ConcurrentQueue
{
private:
std::queue<Data> the_queue;
mutable boost::mutex the_mutex;
boost::condition_variable the_condition_variable;
public:
void push(Data const& data)
{
boost::mutex::scoped_lock lock(the_mutex);
the_queue.push(data);
lock.unlock();
the_condition_variable.notify_one();
}
bool empty() const
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.empty();
}
bool try_pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
if(the_queue.empty())
{
return false;
}
popped_value=the_queue.front();
the_queue.pop();
return true;
}
void wait_and_pop(Data& popped_value)
{
boost::mutex::scoped_lock lock(the_mutex);
while(the_queue.empty())
{
the_condition_variable.wait(lock);
}
popped_value=the_queue.front();
the_queue.pop();
}
Data& front()
{
boost::mutex::scoped_lock lock(the_mutex);
return the_queue.front();
}
};
我的问题是:如何清理AddSampleToQueue和AddFrameToQueue以免它们造成内存泄漏?
BTW:我对所有这些C ++共享/自动内容都很陌生。所以说一个初学者。所以请提供至少可以集成到我的代码中的代码示例 - 因为我提供了所有代码。因此,如果您知道该怎么做 - 请尝试将您的知识整合到我的示例中。谢谢。如果你能告诉我如何不使用共享/自动ptrs,我会非常高兴。
答案 0 :(得分:3)
答案 1 :(得分:3)
答案 2 :(得分:3)
如果,当帧被添加到队列中时,数据数组的所有权将转移到样本,free或者删除样本的析构函数中的[]。
您也可能希望使用移动构造函数,这样您就可以拥有ConcurrentQueue<VideoSample>
而不是ConcurrentQueue<VideoSample*>
的队列,这样可以使您自动排队和出列的样本。
或者,如果您控制将数据推送到队列的内容,请使用vector或boost :: array而不是C样式数组。
如果你真的只想要一件东西,那么使用队列也有点奇怪。拥有一个受互斥锁和条件变量保护的变量就可以了。
答案 3 :(得分:3)
很多其他人都提出了共享指针 我没有看到在这里不使用队列的共享指针而是的原因。毕竟,你只允许一帧。它们优雅地支持锁定,只需要一点点躲避就可以使线程安全且简单。你真的没有机会发现我可以看到的循环引用,所以你应该基本上这样。
或者,这听起来像一个漂亮的循环缓冲区的工作。这样你就可以完全避免裸char数组。 Boost优雅地实现了一个,只需要一点原始同步,你就应该能够使它适合你的目的。特别值得注意的是,这将使扩展您的应用程序以处理在线数据处理也相对简单。
如果您有兴趣,我会破解一些示例代码。
答案 4 :(得分:1)
首先,我会将ConcurrentQueue<VideoSample * > VideoSamples;
更改为
ConcurrentQueue<VideoSample> VideoSamples;
您不需要此指针。把剩下的指针转向智能指针,你就完全了!
答案 5 :(得分:1)
valgrind将帮助您找到程序中几乎任何内存泄漏。虽然正如其他人指出的那样,你应该使用shared_ptrs。
答案 6 :(得分:1)
您的代码
VideoSample * newVideoSample = new VideoSample;
VideoSamples.try_pop(newVideoSample);
是内存泄漏。如果try_pop成功,它将覆盖newVideoSample中的指针,并且您之前创建的实例的引用将永远丢失!
答案 7 :(得分:1)
将您的功能更改为以下功能,并在您分配内存的其他位置执行类似的更改。
void VideoEncoder :: AddFrameToQueue(const unsigned char * buf,int size) {
VideoSample * newVideoSample;
if(!VideoSamples.try_pop(newVideoSample))
{
newVideoSample = new VideoSample;
}
else
{
delete buff;
}
newVideoSample->buffer = buf;
newVideoSample->len = size;
VideoSamples.push(newVideoSample);
}
我也不能阻止自己提出这个问题。当你只想要一个项目在队列中时,为什么你需要排队呢。
答案 8 :(得分:1)
我不确定我是否理解这一切,但无论如何我都会试一试。
AddFrameToQueue
函数所以显然你一次想要队列中的一个帧,这意味着你可能根本不需要队列。无论如何:要么队列中没有帧,你应该创建一个新帧,或者有,你应该覆盖它的buffer
和len
字段:
void VideoEncoder::AddFrameToQueue(const unsigned char *buf, int size )
{
VideoSample * newVideoSample = 0;
if (!VideoSamples.try_pop(newVideoSample))
{
// Nothing in queue yet : we allocate a whole new VideoSample
newVideoSample = new VideoSample;
}
else
{
// Here, you want to release newVideoSample->buffer depending on
// the way it was allocated in the first place : free if malloc'ed,
// delete if new'ed...
}
newVideoSample->buffer = buf;
newVideoSample->len = size;
VideoSamples.push(newVideoSample);
// The VideoSample pointer has been pushed in the queue : we must no delete
// it in order for the queue to keep containing a valid pointer
}
AddSampleToQueue
函数为什么在这个函数的末尾有一个wait_and_pop
:不是应该在UrlWriteData
中发生的流行音乐?我真的不明白这一部分。如果目标是在队列中有一个项目,你可能不需要队列(第2集),但我想你可以使用与AddFrameToQueue
相同的代码。
UrlWriteData
函数此处,数据实际上已从队列中删除,因此您希望在完成编写后立即将其释放。
void VideoEncoder::UrlWriteData()
{
while(1){
switch (AudioSamples.empty()){
case true :
switch(VideoSamples.empty()){
case true : Sleep(5); break;
case false :
VideoSample * newVideoSample;
VideoSamples.wait_and_pop(newVideoSample);
url_write (url_context, (unsigned char *)newVideoSample->buffer, newVideoSample->len);
// Release newVideoSample->buffer using free if malloc'ed, delete
// if new'ed...
delete newVideoSample;
break;
} break;
case false : Sleep(3); break;
}
}
}
这是我无法告诉你的最好的东西,不会破坏整个事情,去寻找智能指针,RAII以及所有那些使C ++成为现实的东西:)