ffmpeg播放器18

62 阅读2分钟

1背景

上一篇分析了HRingBuf,这一篇分析HFrameBuf。

2步骤

2.1 HFrameBuf

在src/util/hframe.h中有如下代码

class HFrameBuf : public HRingBuf {
 public:
    enum CacheFullPolicy {
        SQUEEZE,
        DISCARD,
    } policy;

    HFrameBuf() : HRingBuf() {
        cache_num = DEFAULT_FRAME_CACHENUM;
        policy = SQUEEZE;
    }

    void setCache(int num) {cache_num = num;}
    void setPolicy(CacheFullPolicy policy) {this->policy = policy;}

    int push(HFrame* pFrame);
    int pop(HFrame* pFrame);
    void clear();

    int         cache_num;
    FrameStats  frame_stats;
    FrameInfo   frame_info;
    std::deque<HFrame> frames;
    std::mutex         mutex;
};

上面声明了三个方法push,pop,clear

2.1.1 push

int HFrameBuf::push(HFrame* pFrame) {
    if (pFrame->isNull())
        return -10;

    frame_stats.push_cnt++;

    std::lock_guard<std::mutex> locker(mutex);

    if (frames.size() >= (size_t)cache_num) {
        hlogd("frame cache full!");
        if (policy == HFrameBuf::DISCARD) {
            return -20;     // note: cache full, discard frame
        }

        HFrame& frame = frames.front();
        frames.pop_front();
        free(frame.buf.len);
        if (frame.userdata) {
            hlogd("free userdata");
            ::free(frame.userdata);
            frame.userdata = NULL;
        }
    }

    int ret = 0;
    if (isNull()) {
        resize(pFrame->buf.len * cache_num);
        ret = 1;    // note: first push

        frame_info.w = pFrame->w;
        frame_info.h = pFrame->h;
        frame_info.type = pFrame->type;
        frame_info.bpp  = pFrame->bpp;
    }

    HFrame frame;
    frame.buf.base = alloc(pFrame->buf.len);
    frame.buf.len  = pFrame->buf.len;
    frame.copy(*pFrame);
    frames.push_back(frame);
    frame_stats.push_ok_cnt++;

    return ret;
}

C++标准库提供了类模板std::lock_guard<>,针对互斥类融合实现了RAII手法:在构造时给互斥加锁,在析构时解锁,从而保证互斥总被正确解锁。

std::scoped_lock是C++17中引入的,用于替代std::lock_guard,它可以同时管理多个互斥量,而无须担心死锁的问题。它在构造时尝试锁定所有给定的互斥量,并在析构时释放它们。

std::unique_lock比std::lock_guard更灵活。它不仅能自动管理锁的获取和释放,还可以在运行时手动控制锁的获取和释放。

上面可以保证push,pop,clear互斥。

利用互斥保护共享数据并不太简单,我们不能把std::lock_guard对象化作“铁拳”,对准每个成员函数施予“重击”。 一旦出现游离的指针或引用,这种保护就全部形同虚设。 不管成员函数通过什么“形式”——无论是返回值,还是输出参数(out parameter)——向调用者返回指针或引用,指向受保护的共享数据,就会危及共享数据安全。

上面代码进行了如下操作

  • 检查是否满了
  • 检查是否为空
  • 深拷贝frame
  • 加入队列

2.1.2 pop以及clear

int HFrameBuf::pop(HFrame* pFrame) {
    frame_stats.pop_cnt++;

    std::lock_guard<std::mutex> locker(mutex);

    if (isNull())
        return -10;

    if (frames.size() == 0) {
        hlogd("frame cache empty!");
        return -20;
    }

    HFrame& frame = frames.front();
    frames.pop_front();
    free(frame.buf.len);

    if (frame.isNull())
        return -30;

    pFrame->copy(frame);
    frame_stats.pop_ok_cnt++;

    return 0;
}

void HFrameBuf::clear() {
    std::lock_guard<std::mutex> locker(mutex);
    frames.clear();
    HRingBuf::clear();
}

上面进行了如下操作

  • 判断是否为空
  • 判断是否有数据
  • 出队列
  • 判断数据是否取到
  • 深拷贝

frame_stats.pop_cnt没加锁,没用到也无所谓