此处是接着上一篇文章,c++以Void*释放对象会怎样?最后留下问题的最终解决方案。声明:源码来自Google NKD!
![](https://img.haomeiwen.com/i2141431/6dd82df362bf0cc1.png)
Google源码我就不贴出来了,有兴趣直接查看NDK。直接贴我改造后的代码。
消息对象基类(post的消息必须继承该基类):
//post 消息对象基类
class LoopMsgObj
{
public:
LoopMsgObj(){}
~LoopMsgObj(){}
};
是的,LoopMsgObj什么都没做。
Looper类声明:
class looper {
public:
looper();
virtual ~looper();
//flush 是否清空消息队列
void post(int what, LoopMsgObj *data, bool flush = false);
void quit();
virtual void handle(int what, LoopMsgObj *data);
private:
virtual void addmsg(loopermessage *msg, bool flush);
static void* trampoline(void* p);
void loop();
protected:
std::deque< loopermessage * > _msgQueue;
pthread_t worker;
sem_t headwriteprotect;
sem_t headdataavailable;
bool running;
};
Looper实现:
void* looper::trampoline(void* p) {
LOGV("at looper trampoline");
((looper*)p)->loop();
return NULL;
}
looper::looper() {
LOGV("at looper create");
// head = NULL;
sem_init(&headdataavailable, 0, 0);
sem_init(&headwriteprotect, 0, 1);
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&worker, &attr, trampoline, this);
running = true;
}
looper::~looper() {
if (running) {
LOGV("Looper deleted while still running. Some messages will not be processed");
quit();
}
}
//
void looper::post(int what, LoopMsgObj *data, bool flush) {
loopermessage *msg = new loopermessage();
msg->what = what;
msg->obj = data;
msg->quit = false;
addmsg(msg, flush);
}
void looper::addmsg(loopermessage *msg, bool flush) {
sem_wait(&headwriteprotect);
if (flush) {
_msgQueue.clear();
}
_msgQueue.push_back(msg);
LOGV("post msg %d", msg->what);
sem_post(&headwriteprotect);
sem_post(&headdataavailable);
}
void looper::loop() {
LOGV("at loop");
while(true)
{
// wait for available message
sem_wait(&headdataavailable);
LOGV("headdataavailable");
// get next available message
sem_wait(&headwriteprotect);
LOGV("headwriteprotect");
if(_msgQueue.size() > 0)
{
loopermessage *msg = _msgQueue.front();
_msgQueue.pop_front();
//quit 退出
if (msg->quit)
{
delete msg->obj;
delete msg;
while(_msgQueue.size() > 0)
{
msg = _msgQueue.front();
_msgQueue.pop_front();
delete msg->obj;
delete msg;
}
sem_post(&headwriteprotect);
return;
}
sem_post(&headwriteprotect);
LOGV("processing msg %d", msg->what);
handle(msg->what, msg->obj);
delete msg;
}
else
{
LOGV("no msg");
sem_post(&headwriteprotect);
continue;
}
}
}
void looper::quit() {
LOGV("quit");
loopermessage *msg = new loopermessage();
msg->what = 0;
msg->obj = NULL;
msg->quit = true;
addmsg(msg, true);
void *retval;
pthread_join(worker, &retval);
sem_destroy(&headdataavailable);
sem_destroy(&headwriteprotect);
running = false;
}
void looper::handle(int what, LoopMsgObj* obj) {
LOGV("dropping msg %d %p", what, obj);
}
这里主要用sem_post和sem_wait信号量机制来保证loop线程在没有消息的时候处于睡眠状态,不会空转,关于信号量详细介绍可以看这里。
里面用STL的deque容器来装消息,当主动post一个LoopMsgObj,就push_back到_msgQueue容器中。loop不停的从_msgQueue容器中pop_front一个消息,丢给handle去处理。改造后Looper源码地址。
上面的Looper能满足大多数情况,但是在某些情况handle消息速度跟不上post消息的时候就需要动态丢数据以满足需要。
固定消息长度的FixedLoop类,继承Looper,实现代码:
namespace WeiYu
{
FixedLoop::FixedLoop(int MaxMsgLen):_MaxMsgLen(MaxMsgLen),looper()
{
}
void FixedLoop::addmsg(loopermessage *msg, bool flush)
{
sem_wait(&headwriteprotect);
if (flush) {
_msgQueue.clear();
}
if(_msgQueue.size() >= _MaxMsgLen) //移除一个消息
{
loopermessage *tempMsg = _msgQueue.front();
_msgQueue.pop_front();
delete tempMsg->obj;
delete tempMsg;
}
_msgQueue.push_back(msg);
sem_post(&headwriteprotect);
sem_post(&headdataavailable);
}
}
代码很简单,在addmsg的时候判断容器_msgQueue大小是否达到_MaxMsgLen,如果达到_MaxMsgLen,就主动丢掉_msgQueue的front消息,然后再把新消息push_back进容器。
我拿FixedLoop这个类做视频编码类,摄像头采集图像数据post进去,然后在handle里面编码h264。我在handle里面尝试做cpu美颜,编码速度在低端手机大概一秒8帧左右,摄像头采集一秒15帧,严重跟不上采集速度,动态丢帧效果非常明显。使用FixedLoop安卓视频编码h264源码地址。
本来我也用FixedLoop来把编码后的视频信息推流到RTMP服务器,无奈在4G网络不好的情况下,播放推流的视频经常出现马赛克。研究了一段时间原因,才发现是关键帧被丢掉了,关键帧后面的P帧却没有丢掉导致的。无奈,只要再封装一个NaluLoop类,实现代码:
namespace WeiYu
{
NaluLoop::NaluLoop(int QueueNaluLen):_MaxNalu(QueueNaluLen),looper()
{}
//队列里面既有音频,也有视频
void NaluLoop::addmsg(loopermessage *msg, bool flush)
{
sem_wait(&headwriteprotect);
if (flush) {
_msgQueue.clear();
}
if(_msgQueue.size() >= _MaxNalu) //移除消息,直到下一个I帧,或者队列为空
{
loopermessage *tempMsg = _msgQueue.front();
_msgQueue.pop_front();
delete (NaluStruct*)tempMsg->obj;
delete tempMsg;
while(_msgQueue.size() > 0)
{
tempMsg = _msgQueue.front();
if(((NaluStruct*)tempMsg->obj)->type == 5)
{
break;
}
_msgQueue.pop_front();
delete tempMsg->obj;
delete tempMsg;
}
}
_msgQueue.push_back(msg);
sem_post(&headwriteprotect);
sem_post(&headdataavailable);
}
}
代码也非常简单,在addmsg时打到限制条件,就把_msgQueue中第一个关键帧前边所有的视频(P帧)和音频数据都丢弃。是用NaluLoop封装的RTMP推流器类源码。
用手机连接电脑wifi,然后限制网速,推流速度更不上编码速度,测试效果还不错。
网友评论