c++ Post Handler

此处是接着上一篇文章,c++以Void*释放对象会怎样?最后留下问题的最终解决方案。声明:源码来自Google NKD!

c++ Post Handler_第1张图片
Looper目录结构位置
如果直接拿Google源码来是用,请注意,里面有bug,head没有初始化为NULL,是个野指针!!!!
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,然后限制网速,推流速度更不上编码速度,测试效果还不错。

你可能感兴趣的:(c++ Post Handler)