ffmpeg播放器20(从17-19的实现)

70 阅读8分钟

1背景

在17里面整理了下之前调用的顺序,如下

  1. 线程start中调用了run
  2. 线程run中执行了doTask
  3. 子类hffplayer的dotask中解析并给data赋值
  4. hframe在open的时候指向了data
  5. push_frame(&hframe);
  6. HVideoPlayer中使用HFrameBuf
  7. HFrameBuf使用了HRingBuf
  8. HRingBuf继承了HBuf

在16结尾处理了HRingBuf,这一篇继续之后的处理

2 步骤

2.1 HFrameBuf

右键添加新文件

image.png

然后在头文件追加如下代码

class HFrameBuf : public HRingBuf
{
public:
    HFrameBuf();


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

    int cache_num;
    std::deque<HFrame> frames;
    std::mutex mutex;
};

然后选中 push,pop,clear创建定义

image.png

然后添加如下代码

#include "hframebuf.h"

HFrameBuf::HFrameBuf() {
    cache_num=DEFAUTL_FRAME_CACHENUM;
}

int HFrameBuf::push(HFrame *pFrame)
{
    std::lock_guard<std::mutex> locker(this->mutex);
    if(pFrame->isNull())
        return -10;


    if(frames.size()>=(size_t)cache_num){
        HFrame& frame=frames.front();
        frames.pop_front();
        free(frame.buf.len);
        if(frame.userdata){
            ::free(frame.userdata);
            frame.userdata=NULL;
        }
    }
    int ret=0;
    if(isNull()){
        resize(pFrame->buf.len*cache_num);
        ret=1;
    }

    HFrame frame;
    frame.buf.base=alloc(pFrame->buf.len);
    frame.buf.len=pFrame->buf.len;
    frame.copy(*pFrame);
    frames.push_back(frame);
    return ret;
}

int HFrameBuf::pop(HFrame *pFrame)
{
    std::lock_guard<std::mutex> locker(this->mutex);

    if(isNull())
        return -10;


    if(frames.size()==0){
        return -20;
    }

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

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

    pFrame->copy(frame);
    return 0;
}

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

其他在hbuf.h中需要补充

    bool isNull();
    void copy(void* data,size_t len);

在hbuf.cpp中需要补充

void HBuf::copy(void *data, size_t len)
{
    resize(len);
    memcmp(base,data,len);
}

bool HBuf::isNull()
{
    return base==NULL||len==0;
}

在hframe.h中补充


    uint64_t ts;
    int64_t useridx;
    void* userdata;

    HFrame(){
        w=h=bpp=type=0;
        ts=0;
        this->useridx=-1;
        this->userdata=NULL;
    }

    bool isNull();
    void copy(const HFrame& rhs);

在hframe.cpp中补充

bool HFrame::isNull()
{
    return w==0||h==0||buf.isNull();
}

void HFrame::copy(const HFrame &rhs)
{
    w=rhs.w;
    this->h=rhs.h;
    this->bpp=rhs.bpp;
    this->type=rhs.type;

    this->ts=rhs.ts;
    this->useridx=rhs.useridx;
    this->userdata=rhs.userdata;

    buf.copy(rhs.buf.base,rhs.buf.len);
}

2.2 start开启线程后的doPrepare,还有后续的run

hvideoplayer.h 中补充如下

    virtual int stop()=0;
    virtual int pause()=0;
    virtual int resume()=0;

    void clear_frame_cache(){
        this->frame_buf.clear();
    }

    int push_frame(HFrame* pFrame){
        return frame_buf.push(pFrame);
    }

    int pop_frame(HFrame* pFrame){
        return frame_buf.pop(pFrame);
    }
    
protected:
    HFrameBuf frame_buf;

hthread.h 中补充如下

public:
    enum Status{
        STOP,
        RUNNING,
        PAUSE
    };

    virtual int start(){
        if(status==STOP){
            thread = std::thread([this]{
                if(!doPrepare())return;
                setStatus(RUNNING);
                run();
                setStatus(STOP);
            });
        }
        return 0;
    }

    void setStatus(Status stat){
        status=stat;
    }

    virtual void run(){
        while(status!= STOP){
            doTask();
        }
    }

    virtual bool doPrepare(){
        return true;
    }
    virtual void doTask(){};

    virtual int stop() {
        if (status != STOP) {
            setStatus(STOP);
        }
        if (thread.joinable()) {
            thread.join();  // wait thread exit
        }
        return 0;
    }

    virtual int pause() {
        if (status == RUNNING) {
            setStatus(PAUSE);
        }
        return 0;
    }

    virtual int resume() {
        if (status == PAUSE) {
            setStatus(RUNNING);
        }
        return 0;
    }


    std::thread thread;
    std::atomic<Status> status;
};

hffplayer.h 中重载doTask

    // HThread interface
public:
    virtual void doTask() override;

    int quit;

    // HVideoPlayer interface
public:
    virtual int stop() override;
    virtual int pause() override;
    virtual int resume() override;

然后doTask重构添加定义 hffplayer.cpp

void HFFPlayer::doTask()
{
    while (!this->quit) {
        av_init_packet(packet);

        int ret=av_read_frame(this->fmt_ctx,this->packet);

        if(ret!=0){
            if(!this->quit){

            }
            av_packet_unref(this->packet);
            return;
        }


        if(this->packet->stream_index!= this->video_stream_index){
            continue;
        }


        ret=avcodec_send_packet(this->codec_ctx,this->packet);
        if(ret!=0){
            av_packet_unref(this->packet);
            return;
        }

        ret= avcodec_receive_frame(this->codec_ctx,this->frame);
        if(ret!=0){

        }else{
            break;
        }
    }

    if(this->sws_ctx){
        int h=sws_scale(this->sws_ctx,this->frame->data,
                          this->frame->linesize,0,this->frame->height,this->data,this->linesize);
        if(h<=0 || h != this->frame->height){
            return;
        }
    }

    this->push_frame(&this->hframe);
}

int HFFPlayer::stop()
{
    return HThread::stop();
}

int HFFPlayer::pause()
{
 return HThread::pause();
}

int HFFPlayer::resume()
{
 return HThread::resume();
}

上面打断点并运行 image.png

2.3 gl渲染 的类库

参考ffmpeg播放器10 中glew的,分别添加include以及lib和bin下面,到项目文件夹

        xplayer/3rd/bin/msvc/glew32.dll
        xplayer/3rd/include/GL/
        xplayer/3rd/lib/msvc/glew32.lib
        xplayer/3rd/lib/msvc/glew32s.lib

然后在pro中增加如下

INCLUDEPATH +=$$PWD/3rd/include
INCLUDEPATH +=$$PWD/3rd/include/GL
win32 {
    win32-msvc{
        LIBS += -lavformat      \
                -lavdevice      \
                -lavcodec       \
                -lswresample    \
                -lswscale       \
                -lavutil        \

        LIBS += -lopengl32 -lglu32
        LIBS += -lglew32

        DESTDIR = $$PWD/3rd/bin/msvc
        LIBS += -L$$PWD/3rd/lib/msvc


    }
}

image.png

2.4 一系列修改

typedef enum{
    PIX_FMT_NONE = 0,

    PIX_FMT_GRAY,       // YYYYYYYY

    PIX_FMT_YUV_FIRST = 100,
    PIX_FMT_YUV_PLANAR_FIRST = 200,
    PIX_FMT_IYUV,       // YYYYYYYYUUVV
    PIX_FMT_YV12,       // YYYYVVYYVVUU
    PIX_FMT_NV12,       // YYUVYYYYUVUV
    PIX_FMT_NV21,       // YYVUYYYYVUVU
    PIX_FMT_YUV_PLANAR_LAST,
    PIX_FMT_YUV_PACKED_FIRST = 300,
    PIX_FMT_YUY2,       // YUYVYUYV
    PIX_FMT_YVYU,       // YVYUYVYU
    PIX_FMT_UYVY,       // UYVYUYVY
    PIX_FMT_YUV_PACKED_LAST,
    PIX_FMT_YUV_LAST,

    PIX_FMT_RGB_FIRST = 400,
    PIX_FMT_RGB,        // RGBRGB
    PIX_FMT_BGR,        // BGRBGR
    PIX_FMT_RGBA,       // RGBARGBA
    PIX_FMT_BGRA,       // BGRABGRA
    PIX_FMT_ARGB,       // ARGBARGB
    PIX_FMT_ABGR,       // ABGRABGR
    PIX_FMT_RGB_LAST,
} pix_fmt_e;

hbuf.cpp 修改copy代码如下

void HBuf::copy(void *data, size_t len)
{
    resize(len);
    memcpy(base,data,len);
}

glwnd.h 添加paintGL


    // QOpenGLWidget interface
protected:
    virtual void paintGL() override;

glwnd.cpp添加定义

void GLWnd::paintGL()
{
    HGLWidget::paintGL();

    if(this->last_frame.isNull()){

    }else{
        drawFrame(&this->last_frame);
    }
}

hframebuf.cpp 修改pop实现

int HFrameBuf::pop(HFrame *pFrame)
{
    std::lock_guard<std::mutex> locker(this->mutex);

    if(isNull())
        return -10;


    if(frames.size()==0){
        return -20;
    }

    HFrame frame = std::move(frames.front());
    frames.pop_front();

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

    pFrame->copy(frame);
    return 0;
}

hvideoplayer.h 中添加fps,并初始化

#define DEFAULT_FPS         25

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

hvideowidget.cpp 修改onOpenSucceed方法

void HVideoWidget::onOpenSucceed()
{
    timer->start(1000 / (pImpl_player->fps));
    setAspectRatio(aspect_ratio);
}

2.5 hglwidget.h的修改

#ifndef HGLWIDGET_H
#define HGLWIDGET_H

#include "glew.h"
#include "QtOpenGLWidgets/qopenglwidget.h"
#include "hframe.h"
typedef struct GLTexture_s {
    unsigned int id;  // for glGenTextures
    HFrame frame;
} GLTexture;

#define CLR_B(c)    (c         & 0xff)
#define CLR_G(c)    ((c >> 8)  & 0xff)
#define CLR_R(c)    ((c >> 16) & 0xff)
#define CLR_A(c)    ((c >> 24) & 0xff)
#define ARGB(a, r, g, b) MAKE_FOURCC(a, r, g, b)

#ifndef ABS
#define ABS(n)  ((n) > 0 ? (n) : -(n))
#endif
#define FLOAT_PRECISION     1e-6
#define FLOAT_EQUAL_ZERO(f) (ABS(f) < FLOAT_PRECISION)

void bindTexture(GLTexture* tex, QImage* img);
class HGLWidget : public QOpenGLWidget
{
public:
    explicit HGLWidget(QWidget *parent = nullptr);


    // ratio = 0 means spread
    void setAspectRatio(double ratio);

    void drawFrame(HFrame *pFrame);
    void drawTexture(QRect rc, GLTexture *tex);
    void drawRect(QRect rc, QColor clr, int line_width = 1, bool bFill = false);
    void drawText(QPoint lb, const char* text, int fontsize, QColor clr);
signals:

    // QOpenGLWidget interface
protected:


    // QOpenGLWidget interface
protected:
    virtual void initializeGL() override;
    virtual void resizeGL(int w, int h) override;
    virtual void paintGL() override;


    void setVertices(double ratio);
    void setVertices(QRect rc);

    static void loadYUVShader();
    void initVAO();
    void initYUV();

    void drawYUV(HFrame* pFrame);

protected:
    static std::atomic_flag s_glew_init;
    static GLuint prog_yuv;
    static GLuint texUniformY;
    static GLuint texUniformU;
    static GLuint texUniformV;
    GLuint  tex_yuv[3];

    double  aspect_ratio;
    GLfloat vertices[8];
    GLfloat textures[8];

    // NOTE: QPainter used 3 VertexAttribArray
    enum VER_ATTR {
        VER_ATTR_VER = 3,
        VER_ATTR_TEX,
    };

};

#endif // HGLWIDGET_H

以及cpp中的实现

#include "hglwidget.h"
#include "avdef.h"



static int glPixFmt(int type) {
    switch (type) {
    case PIX_FMT_BGR:  return GL_BGR;
    case PIX_FMT_RGB:  return GL_RGB;
    case PIX_FMT_BGRA: return GL_BGRA;
    case PIX_FMT_RGBA: return GL_RGBA;
    }
    return -1;
}

void bindTexture(GLTexture* tex, QImage* img) {
    if (img->format() != QImage::Format_ARGB32)
        return;

    glGenTextures(1, &tex->id);
    glBindTexture(GL_TEXTURE_2D, tex->id);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    tex->frame.w = img->width();
    tex->frame.h = img->height();
    tex->frame.type = GL_BGRA;
    tex->frame.bpp = img->bitPlaneCount();
    // gluBuild2DMipmaps(GL_TEXTURE_2D, tex->frame.bpp/8, tex->frame.w, tex->frame.h, tex->frame.type, GL_UNSIGNED_BYTE, img->bits());
    glTexImage2D(GL_TEXTURE_2D, 0, tex->frame.bpp/8, tex->frame.w, tex->frame.h, 0, tex->frame.type, GL_UNSIGNED_BYTE, img->bits());
}
HGLWidget::HGLWidget(QWidget *parent)
    : QOpenGLWidget{parent}
{
    aspect_ratio = 0.0;
    setVertices(1.0);

    GLfloat tmp[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
    };

    // reverse
    /*
    GLfloat tmp[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };
    */
    memcpy(textures, tmp, sizeof(GLfloat)*8);
}

std::atomic_flag HGLWidget::s_glew_init = ATOMIC_FLAG_INIT;
GLuint HGLWidget::prog_yuv;
GLuint HGLWidget::texUniformY;
GLuint HGLWidget::texUniformU;
GLuint HGLWidget::texUniformV;

void HGLWidget::initializeGL()
{
    if (glewInit() != 0) {
        s_glew_init.clear();
        qFatal("glewInit failed");
        return;
    }

    initVAO();

    loadYUVShader();
    initYUV();
}

void HGLWidget::setAspectRatio(double ratio) {
    aspect_ratio = ratio;
    if (aspect_ratio==0) {
        setVertices(1.0);
    }
    else {
        setVertices((double)height()/(double)width() * aspect_ratio);
    }
}


void HGLWidget::setVertices(double ratio) {
    GLfloat w = 1.0, h = 1.0;
    if (ratio < 1.0) {
        w = ratio;
    }
    else {
        h = 1.0 / ratio;
    }

    GLfloat tmp[] = {
        -w, -h,
        w, -h,
        -w,  h,
        w,  h,
    };

    memcpy(vertices, tmp, sizeof(GLfloat)*8);
}

void HGLWidget::setVertices(QRect rc) {
    int wnd_w = width();
    int wnd_h = height();
    if (wnd_w <= 0 || wnd_h <= 0) {
        return;
    }
    GLfloat left = (GLfloat)rc.left() * 2 / wnd_w - 1;
    GLfloat right = (GLfloat)(rc.right()+1) * 2 / wnd_w - 1;
    GLfloat top = 1 - (GLfloat)rc.top() * 2 / wnd_h;
    GLfloat bottom = 1 - (GLfloat)(rc.bottom()+1) * 2 / wnd_h;
    qDebug("l=%d r=%d t=%d b=%d", rc.left(), rc.right(), rc.top(), rc.bottom());
    qDebug("l=%f r=%f t=%f b=%f", left, right, top, bottom);
    GLfloat tmp[] = {
        left,  bottom,
        right, bottom,
        left,  top,
        right, top
    };

    memcpy(vertices, tmp, sizeof(GLfloat)*8);
}

void HGLWidget::loadYUVShader() {
    GLuint vs = glCreateShader(GL_VERTEX_SHADER);
    GLuint fs = glCreateShader(GL_FRAGMENT_SHADER);

    char szVS[] = "				\
        attribute vec4 verIn;		\
        attribute vec2 texIn;		\
        varying vec2 texOut;		\
                                \
        void main(){				\
            gl_Position = verIn;	\
            texOut = texIn;			\
    }							\
        ";
        const GLchar* pszVS = szVS;
    GLint len = strlen(szVS);
    glShaderSource(vs, 1, (const GLchar**)&pszVS, &len);

    char szFS[] = "				\
        varying vec2 texOut;		\
        uniform sampler2D tex_y;	\
        uniform sampler2D tex_u;	\
        uniform sampler2D tex_v;	\
                                \
        void main(){				\
            vec3 yuv;				\
            vec3 rgb;				\
            yuv.x = texture2D(tex_y, texOut).r;			\
            yuv.y = texture2D(tex_u, texOut).r - 0.5;	\
            yuv.z = texture2D(tex_v, texOut).r - 0.5;	\
            rgb = mat3( 1,       1,         1,			\
                   0,       -0.39465,  2.03211,			\
                   1.13983, -0.58060,  0) * yuv;			\
            gl_FragColor = vec4(rgb, 1);				\
    }												\
        ";
        const GLchar* pszFS = szFS;
    len = strlen(szFS);
    glShaderSource(fs, 1, (const GLchar**)&pszFS, &len);

    glCompileShader(vs);
    glCompileShader(fs);

    //#ifdef _DEBUG
    GLint iRet = 0;
    glGetShaderiv(vs, GL_COMPILE_STATUS, &iRet);
    qDebug("vs::GL_COMPILE_STATUS=%d", iRet);
    glGetShaderiv(fs, GL_COMPILE_STATUS, &iRet);
    qDebug("fs::GL_COMPILE_STATUS=%d", iRet);
    //#endif

    prog_yuv = glCreateProgram();

    glAttachShader(prog_yuv, vs);
    glAttachShader(prog_yuv, fs);

    glBindAttribLocation(prog_yuv, VER_ATTR_VER, "verIn");
    glBindAttribLocation(prog_yuv, VER_ATTR_TEX, "texIn");

    glLinkProgram(prog_yuv);

    //#ifdef _DEBUG
    glGetProgramiv(prog_yuv, GL_LINK_STATUS, &iRet);
    qDebug("prog_yuv=%d GL_LINK_STATUS=%d", prog_yuv, iRet);
    //#endif

    glValidateProgram(prog_yuv);

    texUniformY = glGetUniformLocation(prog_yuv, "tex_y");
    texUniformU = glGetUniformLocation(prog_yuv, "tex_u");
    texUniformV = glGetUniformLocation(prog_yuv, "tex_v");

    qDebug("loadYUVShader ok");
}

void HGLWidget::initVAO() {
    glVertexAttribPointer(VER_ATTR_VER, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(VER_ATTR_VER);

    glVertexAttribPointer(VER_ATTR_TEX, 2, GL_FLOAT, GL_FALSE, 0, textures);
    glEnableVertexAttribArray(VER_ATTR_TEX);
}

void HGLWidget::initYUV() {
    glGenTextures(3, tex_yuv);
    for (int i = 0; i < 3; ++i) {
        glBindTexture(GL_TEXTURE_2D, tex_yuv[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
}

void HGLWidget::resizeGL(int w, int h) {
    glViewport(0,0,w,h);
    setAspectRatio(aspect_ratio);
}

void HGLWidget::paintGL() {
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}

void HGLWidget::drawYUV(HFrame* pFrame) {
    assert(pFrame->type == PIX_FMT_IYUV || pFrame->type == PIX_FMT_YV12);

    int w = pFrame->w;
    int h = pFrame->h;
    int y_size = w*h;
    GLubyte* y = (GLubyte*)pFrame->buf.base;
    GLubyte* u = y + y_size;
    GLubyte* v = u + (y_size>>2);
    if (pFrame->type == PIX_FMT_YV12) {
        GLubyte* tmp = u;
        u = v;
        v = tmp;
    }

    glUseProgram(prog_yuv);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex_yuv[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w, h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, y);
    glUniform1i(texUniformY, 0);

    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, tex_yuv[1]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, u);
    glUniform1i(texUniformU, 1);

    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, tex_yuv[2]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, w/2, h/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, v);
    glUniform1i(texUniformV, 2);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glUseProgram(0);
}

void HGLWidget::drawFrame(HFrame *pFrame) {
    if (pFrame->type == PIX_FMT_IYUV || pFrame->type == PIX_FMT_YV12) {
        drawYUV(pFrame);
    }
    else {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glRasterPos3f(-1.0f,1.0f,0);
        glPixelZoom(width()/(float)pFrame->w, -height()/(float)pFrame->h);
        glDrawPixels(pFrame->w, pFrame->h, glPixFmt(pFrame->type), GL_UNSIGNED_BYTE, pFrame->buf.base);
    }
}

void HGLWidget::drawTexture(QRect rc, GLTexture *tex) {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, width(), height(), 0.0, -1.0, 1.0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex->id);

    glEnable(GL_TEXTURE_2D);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glBegin(GL_QUADS);
    glTexCoord2d(0,0);glVertex2i(rc.left(), rc.top());
    glTexCoord2d(1,0);glVertex2i(rc.right(), rc.top());
    glTexCoord2d(1,1);glVertex2i(rc.right(), rc.bottom());
    glTexCoord2d(0,1);glVertex2i(rc.left(), rc.bottom());
    glEnd();

    glDisable(GL_TEXTURE_2D);
    glDisable(GL_BLEND);
}


void HGLWidget::drawRect(QRect rc, QColor clr, int line_width, bool bFill) {
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, width(), height(), 0.0, -1.0, 1.0);

    if (bFill) {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    else {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    glLineWidth(line_width);

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glColor4ub(clr.red(), clr.green(), clr.blue(), clr.alpha());
    glRecti(rc.left(), rc.top(), rc.right(), rc.bottom());
    glColor4ub(255,255,255,255);

    glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    glDisable(GL_BLEND);
}

#include <QPainter>
void HGLWidget::drawText(QPoint lb, const char* text, int fontsize, QColor clr) {
    QPainter painter(this);
    QFont font = painter.font();
    font.setPointSize(fontsize);
    painter.setFont(font);
    painter.setPen(clr);
    painter.drawText(lb, text);
}

2.6 最后效果(只有一帧,doTask中只处理了一次)

image.png

当前代码分支

github.com/xcyxiner/xp…

总代码 github.com/xcyxiner/xp…

3 未完待续,下次处理完视频,再使用SDL渲染