ffmpeg播放器16(从13-15的实现)-下

64 阅读4分钟

1背景

上面这几篇,是使用qt的gl渲染的。ffmpeg播放器16上里面,新建了三个文件,这一篇继续后续的。

源码存放位置 github.com/xcyxiner/xp…

2步骤

2.1 HVideoWnd 补充成员函数

双击 hvideownd.h 的,添加如下代码

#include "hframe.h"

public:
    virtual void setGeometry(const QRect& rc) = 0;
    virtual void update() = 0;

public:
    HFrame last_frame;
};

2.2 GLWnd 继承 HVideoWnd 的方法

双击glwnd.h,选中HVideoWnd,重构,插入虚方法

image.png

效果如下

image.png

再添加定义,选中,右键

image.png

最终效果如下

image.png

补充完善代码

void GLWnd::setGeometry(const QRect &rc)
{
    HGLWidget::setGeometry(rc);
}

void GLWnd::update()
{
    HGLWidget::update();
}

2.3 hvideowidget.h 中声明HVideoWnd

双击 hvideowidget.h,添加如下代码

private:
    HVideoWnd *videownd;

image.png

然后在cpp中初始化

#include "glwnd.h"

HVideoWidget::HVideoWidget(QWidget *parent)
    : QFrame(parent)
    , ui(new Ui::HVideoWidget)
{
    ui->setupUi(this);
    this->videownd=new GLWnd(this);
}

image.png

运行效果如下

image.png

2.4 setGeometry

上面2.1里两个方法,其中一个是setGeometry,这个是调整界面大小的。之前播放器12 一直是start里执行线程的。 线程没执行出问题,会触发成功事件。 在hvideowidget.h中添加如下,然后右键,重构,添加定义

public slots:

    void onOpenSucceed();

在hvideowidget.cpp中的start中,添加如下代码

void HVideoWidget::start()
{
    this->pImpl_player=new HFFPlayer();
    this->pImpl_player->set_media(this->media);
    int ret= this->pImpl_player->start();
    if(ret!=0){

    }else{
        //
        onOpenSucceed();
    }
}

void HVideoWidget::onOpenSucceed()
{

}

然后追加onOpenSucceed的实现

这里avdef.h先添加一个枚举类型

typedef enum{
    ASPECT_FULL,//100%
    ASPECT_PERCENT,         // 50%
    ASPECT_ORIGINAL_RATIO,  // w:h
    ASPECT_ORIGINAL_SIZE,   // wxh
    ASPECT_CUSTOM_RATIO,    // 4:3 16:9 ...
    ASPECT_CUSTOM_SIZE,     // 1280x720 640*480 ..
}aspect_ratio_e;

typedef struct aspect_ratio_s {
    aspect_ratio_e type;
    int w, h;
} aspect_ratio_t;

然后在hvideowidget.h中添加声明

public:
    aspect_ratio_t aspect_ratio;

在构造器函数中进行赋值

HVideoWidget::HVideoWidget(QWidget *parent)
    : QFrame(parent)
    , ui(new Ui::HVideoWidget)
{
    ui->setupUi(this);
    this->videownd=new GLWnd(this);
    this->aspect_ratio.type=ASPECT_FULL;
}

在hvideowidget.h中添加setAspectRatio

public slots:
    void open(HMedia& media);
    void start();

    void onOpenSucceed();

    void setAspectRatio(aspect_ratio_t aspect_ratio);

选中setAspectRatio,右键,重构,添加定义

image.png

然后在onOpenSucceed中调用

void HVideoWidget::onOpenSucceed()
{
    setAspectRatio(aspect_ratio);
}

接下来,要去设置hvideoplayer的宽度和高度 双击 HVideoPlayer.h,添加如下代码

public:
    HMedia media;

    int32_t width;
    int32_t height;
    int64_t duration;
    int64_t start_time;
    int eof;
    int error;

image.png 并在构造器中初始化

HVideoPlayer::HVideoPlayer() {
    width = 0;
    height = 0;
    duration = 0;
    start_time = 0;
    eof = 0;
    error = 0;
}

再返回hvideowidget.cpp中添加setAspectRatio的后续代码如下所示

void HVideoWidget::setAspectRatio(aspect_ratio_t aspect_ratio)
{
    this->aspect_ratio = aspect_ratio;
    int border = 1;
    int scr_w = width() - border * 2;
    int scr_h = height() - border * 2;
    if (scr_w <= 0 || scr_h <= 0) return;
    int pic_w = 0;
    int pic_h = 0;
    if (pImpl_player) {
        pic_w = pImpl_player->width;
        pic_h = pImpl_player->height;
    }
    if (pic_w == 0) pic_w = scr_w;
    if (pic_h == 0) pic_h = scr_h;
    // calc videownd rect
    int dst_w, dst_h;
    switch (aspect_ratio.type) {
    case ASPECT_FULL:
        dst_w = scr_w;
        dst_h = scr_h;
        break;
    case ASPECT_PERCENT:
    case ASPECT_ORIGINAL_SIZE:
    case ASPECT_CUSTOM_SIZE:
    case ASPECT_ORIGINAL_RATIO:
    case ASPECT_CUSTOM_RATIO:
        break;
    }
    dst_w = std::min(dst_w, scr_w);
    dst_h = std::min(dst_h, scr_h);
    // align 4
    dst_w = dst_w >> 2 << 2;
    dst_h = dst_h >> 2 << 2;

    int x = border + (scr_w - dst_w) / 2;
    int y = border + (scr_h - dst_h) / 2;
    videownd->setGeometry(QRect(x, y, dst_w, dst_h));
}

运行后的效果

image.png

因为这里的HVideoWidget实例化的时候就是这么大,所以填充满也就上面的效果 image.png

2.5 timer

在hvideowidget.h头文件中添加声明

private:
    HMedia media;
    HVideoPlayer* pImpl_player;

    QTimer*         timer;
};

然后再添加一个onTimerUpdate,关联timer的timeout信号

public slots:
    void open(HMedia& media);
    void start();

    void onOpenSucceed();

    void setAspectRatio(aspect_ratio_t aspect_ratio);

     void onTimerUpdate();

选中onTimerUpdate,右键,重构,添加定义

再在构造函数中初始化timer并触发事件

#include <QTimer>
HVideoWidget::HVideoWidget(QWidget *parent)
    : QFrame(parent)
    , ui(new Ui::HVideoWidget)
{
    ui->setupUi(this);
    this->videownd=new GLWnd(this);
    this->aspect_ratio.type=ASPECT_FULL;

    this->timer=new QTimer(this);
    timer->setTimerType(Qt::PreciseTimer);
    connect(timer,SIGNAL(timeout()),this,SLOT(onTimerUpdate()));
}

然后追加如下代码

void HVideoWidget::onTimerUpdate()
{
    if(this->pImpl_player==NULL)return;
    //提取一帧

    // update video frame
    videownd->update();
}

2.6 HVideoPlayer的视频帧

前面2.5这里要读取一帧视频,前面的代码只涉及了start开启线程后的doPrepare,还有后续的run没处理,这里继续补上。

2.6.1 HRingBuf

HBuf中补充一个构造函数

    HBuf(size_t cap){
        resize(cap);
    }

新增HRingBuf,继承HBuf

image.png

结果如下所示

image.png

添加如下代码

class HRingBuf : public HBuf
{
public:
    HRingBuf();
    HRingBuf(size_t cap);

    virtual ~HRingBuf(){

    }

    char* alloc(size_t len);
    void free(size_t len);
    void clear();

    size_t size() const;

private:
    size_t _head;
    size_t _tail;
    size_t _size;
};

修改构造函数,使用基类初始化

HRingBuf::HRingBuf():HBuf() {

}

HRingBuf::HRingBuf(size_t cap):HBuf(cap)
{

}

选中_size,右键,生成get方法 选中alloc,free,clear,右键,添加定义

最终的cpp代码如下

#include "hringbuf.h"

HRingBuf::HRingBuf():HBuf() {

}

HRingBuf::HRingBuf(size_t cap):HBuf(cap)
{

}

char *HRingBuf::alloc(size_t len)
{

}

void HRingBuf::free(size_t len)
{

}

void HRingBuf::clear()
{

}

size_t HRingBuf::size() const
{
    return _size;
}

补充代码实现

#include "hringbuf.h"

HRingBuf::HRingBuf():HBuf() {
     _head=_tail=_size=0;
}

HRingBuf::HRingBuf(size_t cap):HBuf(cap)
{
    _head=_tail=_size=0;
}

char *HRingBuf::alloc(size_t len)
{
    char* ret=NULL;
    if(_head<_tail || _size==0){
        // [_tail, this->len) && [0, _head)
        if(this->len -_tail>=len){
            ret = base + _tail;
            _tail += len;
            if (_tail == this->len) _tail = 0;
        }
        else if(_head>=len){
            ret=base;
            _tail=len;
        }
    }else{
        // [_tail, _head)
        if(_head-_tail>=len){
            ret=base+_tail;
            _tail+=len;
        }
    }
    _size+=ret?len:0;
    return ret;
}

void HRingBuf::free(size_t len)
{
    _size-=len;
    if(len <= this->len -_head){
        _head+=len;
        if(_head==this->len)_head=0;
    }else{
        _head=len;
    }
}

void HRingBuf::clear()
{
    _head=_tail=_size=0;
}

size_t HRingBuf::size() const
{
    return _size;
}

后面再分析代码的

2.6.2 HFrameBuf

未完待续