ffmpeg播放器3

104 阅读3分钟

1背景

这一篇是继续上一篇hplyaer的后续

2 详细记录

2.1 getIdlePlayer

在HMultiView.cpp中有如下代码

void HMultiView::play(HMedia& media) {
HVideoWidget* player = getIdlePlayer();
player->open(media);

先看这里getIdlePlayer的实现,在当前文件往上找,能找到如下代码

HVideoWidget* HMultiView::getIdlePlayer() {
    for (int i = 0; i < views.size(); ++i) {
        HVideoWidget *player = (HVideoWidget*)views[i];
        if (player->isVisible() && player->status == HVideoWidget::STOP) {
            return player;
        }
    }
    return NULL;
}

这里是根据数组中的可见状态和停止状态来获取可用的播放器对象

然后在头文件HMultiView.h中可以看到

public:
QVector<QWidget*> views;

再cpp中initUI中有如下

void HMultiView::initUI() {

for (int i = 0; i < MV_STYLE_MAXNUM; ++i) {
    HVideoWidget* player = new HVideoWidget(this);
    player->playerid = i+1;
    views.push_back(player);
}

上面player用了HVideoWidget,继续找对应的

2.2 HVideoWidget open

上面2.1中除了初始化,还有一个open的槽

class HVideoWidget : public QFrame
public slots:
    void open(HMedia& media);
private:
    HMedia media;

直接在HVideoWidget.cpp中看open的实现

void HVideoWidget::open(HMedia& media) {
    this->media = media;
    start();
}

这里media还是跟之前一样,只是存储一下信息,接下来看start实现

2.3 HVideoWidget start

先看声明,还是私有槽

public slots:
void start();

再看start定义
```c++
void HVideoWidget::start() {
    if (media.type == MEDIA_TYPE_NONE) {
    QMessageBox::information(this, tr("Info"), tr("Please first set media source, then start."));
    updateUI();
    return;
    }

if (!pImpl_player) {
    pImpl_player = HVideoPlayerFactory::create(media.type);
    pImpl_player->set_media(media);
    pImpl_player->set_event_callback(hplayer_event_callback, this);
    title = media.src.c_str();
    int ret = pImpl_player->start();
    if (ret != 0) {
        onOpenFailed();
    }
    else {
        onOpenSucceed();
    }
    updateUI();
}
else {
    if (status == PAUSE) {
        resume();
    }
}
}

前面的判断可以先跳过,上一篇中是打开的本地文件,可以直接往后面看,pImpl_player用了工厂方法创建对象

先看pImpl_player的声明,从下面的代码看是使用了HVideoPlayer

private:
HVideoPlayer* pImpl_player;

2.4 HVideoPlayerFactory::create

再看工厂方法HVideoPlayerFactory::create 这个在video目录下 #include "HVideoPlayerFactory.h"

class HVideoPlayerFactory
{
public:
    static HVideoPlayer* create(media_type_e type) {
        switch (type) {
        case MEDIA_TYPE_FILE:
        case MEDIA_TYPE_NETWORK:
            return new HFFPlayer;
        case MEDIA_TYPE_CAPTURE:
            // return new HVideoCapture;
            return new HFFPlayer;
        default:
            return NULL;
        }
    }
};

这里静态成员函数有实现,会导致头文件臃肿。在c++17下面,如果在源代码实现,头文件的静态成员函数要 添加 inline 关键字,否则被多个文件包含头文件会导致 odr违规。

  • ODR 是 One Definition Rule(单一定定义规则)的缩写,这是 C++ 的核心规则之一。

上面工厂方法就返回了HFFPlayer的实例对象

2.5 HFFPlayer

在头文件HFFPlayer.h中有如下代码

class HFFPlayer : public HVideoPlayer, public HThread {
public:
HFFPlayer();
~HFFPlayer();

这里用了多继承,合理使用多继承可以高效实现功能组合。

在 C++ 中,多继承(Multiple Inheritance) 指一个派生类(子类)同时从多个基类(父类)继承属性和方法。

可能有的问题

  • (1) 菱形继承(Diamond Problem) 若两个基类(如 HVideoPlayer 和 HThread )有共同的基类,会导致派生类中存在重复的基类成员。 解决方案:使用 虚继承(Virtual Inheritance)
  • (2) 成员名冲突 若两个基类有同名成员,需通过作用域运算符 :: 明确指定
  • (3) 构造函数初始化顺序 基类的构造函数按声明顺序调用(从左到右)

构造器中的实现如下

HFFPlayer::HFFPlayer()
: HVideoPlayer()
, HThread() {
    fmt_opts = NULL;
    codec_opts = NULL;
    fmt_ctx = NULL;
    codec_ctx = NULL;
    packet = NULL;
    frame = NULL;
    sws_ctx = NULL;

    block_starttime = time(NULL);
    block_timeout = DEFAULT_BLOCK_TIMEOUT;
    quit = 0;

    if (!s_ffmpeg_init.test_and_set()) {
        // av_register_all();
        // avcodec_register_all();
        avformat_network_init();
        avdevice_register_all();
        list_devices();
    }
}

2.6 在2.3工厂方法之后的下一篇再处理