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

107 阅读4分钟

1背景

上面这几篇,是使用qt的gl渲染的。上一次12最终到了解码,这一篇继续后续的。

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

2步骤

2.1 图像缩放以及像素格式转换

从8中可以看到解码后进行了 图像缩放以及像素格式转换. 继续在hffplayer.cpp中添加 image.png

先添加dst_pix_fmt的声明

    AVPixelFormat   src_pix_fmt;
    AVPixelFormat   dst_pix_fmt;

image.png

再在cpp中 HFFPlayer::open 添加如下代码

    // 原始视频宽高以及像素格式
    int sw,sh,dw,dh;
    sw=codec_ctx->width;
    sh=codec_ctx->height;
    src_pix_fmt=codec_ctx->pix_fmt;
    if(sw<=0|| sh <=0 || src_pix_fmt==AV_PIX_FMT_NONE){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        if(codec_ctx){
            avcodec_free_context(&codec_ctx);
            codec_ctx=NULL;
        }
        ret=-45;
        return ret;
    }

    //目标视频宽高以及像素格式
    dw=sw&(~3);
    dh=sh&(~3);
    dst_pix_fmt=AV_PIX_FMT_YUV420P;

追加断点并测试

image.png

再在头文件中添加声明

    SwsContext*     sws_ctx;

再在cpp中追加如下代码

    //图像缩放以及像素格式转换
    sws_ctx=sws_getContext(sw,sh,src_pix_fmt,dw,dh,dst_pix_fmt,SWS_BICUBIC,NULL,NULL,NULL);
    if(sws_ctx==NULL){
        if(fmt_ctx){
            avformat_close_input(&fmt_ctx);
            avformat_free_context(fmt_ctx);
            fmt_ctx=NULL;
        }
        if(codec_ctx){
            avcodec_free_context(&codec_ctx);
            codec_ctx=NULL;
        }
        ret=-50;
        return ret;
    }

追加一行输出,打断点

#include <iostream>

std::cout<<ret<<std::endl;

image.png

2.2 frame 视频帧

先添加声明

    AVPacket* packet;
    AVFrame* frame;

然后赋值

    packet=av_packet_alloc();
    frame=av_frame_alloc();

新建一个HFrame文件(c++文件)

image.png

因为这里还使用了HBuf,所以需要再添加一个新文件

image.png

添加成员变量

#include <cstdlib>
class HBuf
{
public:
    char* base;
    size_t len;
    HBuf()  {
        base=NULL;
        len=0;
        cleanup_ = false;
    }
    HBuf(void* data, size_t len)  {
        this->base=(char*)data;
        this->len=len;
        cleanup_ = false;
    }
    ~HBuf(){
        free(base);
        base=NULL;
    }
    char *getBase() const;

    size_t getLen() const;
    
    void resize(size_t cap);

private:
    bool cleanup_;
};

并在hbuf.cpp中添加如下代码

#include "hbuf.h"
#include <cstring>


size_t HBuf::getLen() const
{
    return len;
}

void HBuf::resize(size_t cap)
{
    if(cap==len)return;
    if(base==NULL){
        base=(char*)malloc(cap);
        memset(base,0,cap);
    }else{
        base=(char*)realloc(base,cap);
        if(cap>len){
            memset(base+len,0,cap-len);
        }
    }
    len=cap;
    cleanup_=true;
}

char *HBuf::getBase() const
{
    return base;
}

然后再补全上面的成员变量并初始化,如下所示

#include "hbuf.h"
class HFrame
{
public:
    HBuf buf;
    int w;
    int h;
    int bpp;
    int type;


    HFrame(){
        w=h=bpp=type=0;
    }
};

然后再在hffplayer.h添加申明

#include "hframe.h"

    uint8_t*        data[4];
    int             linesize[4];
    HFrame hframe;

image.png

这里用到PIX_FMT_IYUV,先在avdef.h中添加如下枚举

typedef enum{
    PIX_FMT_NONE=0,
    PIX_FMT_IYUV//YYYY YYYY UUVV
} pix_fmt_e;

继续cpp中的代码

    //视频帧存储
    packet=av_packet_alloc();
    frame=av_frame_alloc();

    hframe.w=dw;
    hframe.h=dh;

    hframe.buf.resize(dw*dh*4);

    if(dst_pix_fmt==AV_PIX_FMT_YUV420P){
        hframe.type=PIX_FMT_IYUV;
        hframe.bpp=12;
        int y_size=dw*dh;
        hframe.buf.len=y_size*3/2;

        data[0]=(uint8_t*)hframe.buf.base;
        data[1]=data[0]+y_size;
        data[2]=data[1]+y_size/4;
        linesize[0]=dw;
        linesize[1]=linesize[2]=dw/2;
    }
    
    return ret;

上面的追加调试的结果

image.png

2.3 gl渲染--HVideoWnd

前面两节属于上一次没写完的代码,这一次补充完整。 之前的start方法执行线程后,上面的只完成了准备工作,后续的run,之后再写。

在播放器11里,提到了HVideoWnd,这里添加一个c++文件,继承QWidget

image.png

image.png

播放器11里提到了工厂方法,这里就跳过,直接使用gl的对象渲染 GLWnd ,后面再添加sdl的渲染

2.4 GLWnd 基类 HGLWidget

从播放器13里知道 GLWnd : public HVideoWnd, HGLWidget ,继承了两个父类。第一个,前面已经添加了,这里再添加后面一个HGLWidget

还是c++文件,名称为HGLWidget,继承QWidget

image.png

hglwidget.h 修改基类为QOpenGLWidget

#include <QWidget>
#include <QtOpenGLWidgets/qopenglwidget.h>
class HGLWidget : public QOpenGLWidget
{
    Q_OBJECT
public:
    explicit HGLWidget(QWidget *parent = nullptr);

signals:
};

同样修改hglwidget.cpp中的构造函数的基类

HGLWidget::HGLWidget(QWidget *parent)
    : QOpenGLWidget{parent}
{}

image.png

image.png

切换到hglwidget.h,选中QOpenGLWidget,右键,重构,插入基类的方法

image.png

只勾选QOpenGLWidget中的initializeGL,resizeGL,paintGL方法,以及勾选下面的添加virtual

image.png

然后选中,右键,重构,添加定义在cpp中

image.png

最终效果如下所示

image.png

image.png

这时编译会报错,需要在pro文件中关联 openglwidgets,如下图所示

image.png

重新构建,就没有报错了

image.png

2.5 GLWnd

添加c++文件,名称为GLWnd,继承QWidget

image.png

image.png

修改基类为 HVideoWnd, HGLWidget

#include "hglwidget.h"
#include "hvideownd.h"

class GLWnd :  public HVideoWnd, HGLWidget

image.png

修改构造器方法

#include "glwnd.h"

GLWnd::GLWnd(QWidget *parent)
    :  HVideoWnd(parent),HGLWidget(parent)
{}

未完待续