FFMpeg opengl显示解码avframe

FFmpeg解码出来avframe,但显示很容易出现乱码,因为为了计算的方便,ffmpeg解码出来的数据,总视根据系统处理器或者系统来补齐为 64、32、16的倍数。这也是我们显示乱码的问题所在。

问题

比如我显示抖音上的视频的宽为368x640, 解码的帧率avFrame,下面是avframe的一些成员变量:

avFrame->format = 0 // 表示是 yuv420p的数据格式

avFrame->width = 364

avFrame->height= 640

然后取数据的时候,

y= avFrame->data[0]

u= avFrame->data[1]

v= avFrame->data[2]

将yuv给opengl渲染就会乱码。

解决方法

将宽度设置为linesize

直接获得data[0]的数据,在创建显示texture的时候以,avFrame-linesize[0]作为一帧的宽度:

 // 直接按照填补的 宽度来显示,效率最高,若视频宽度不是64/32(根据系统位数)倍数,右边会存在边框。
    int w = avFrame->linesize[0];
    int h = avFrame->height;
    unsigned char * y  = avFrame->data[0];
    unsigned char * u  = avFrame->data[1] ;
    unsigned char * v  = avFrame->data[2];
    glProgram->Draw(w,h,y,u ,v);
    LEGL::Get()->Draw();
    av_frame_free(&avFrame);

这样就可以正常显示视频了,取数据也是正常的,但会显示补齐的那部分数据,

缺点:视频右边会有边框。

拷贝有效数据

unsigned char * y = nullptr;
unsigned char * u = nullptr;
unsigned char * v = nullptr;
void getYUV420pData(AVFrame *avFrame)
{
    int w = avFrame->width;
    int h = avFrame->height;

    if(y == nullptr){
        y = new unsigned char[w*h];
    }
    if(u == nullptr){
        u = new unsigned char[w*h/4];
    }
    if(v == nullptr){
        v = new unsigned char[w*h/4];
    }


    int l1 = avFrame->linesize[0];
    int l2 = avFrame->linesize[1];
    int l3 = avFrame->linesize[2];
    for(int i= 0 ; i < h ; i++)
    {
        memcpy(y + w*i,avFrame->data[0] + l1* i, sizeof( unsigned char)*w);
    }
    for(int i= 0 ; i < h/2 ; i++)
    {
        memcpy(u + w/2*i,avFrame->data[1] +  l2 * i, sizeof( unsigned char)*w/2);
        memcpy(v + w/2*i,avFrame->data[2] + l3 * i, sizeof( unsigned char)*w/2);
    }
}

拷贝出有效数据,然后再利用opengl显示图片就可以了。

2022年1月6日新增

如果用代码切割每一帧太慢,可以考虑用opengl来进行裁剪,渲染方式还是如上面一样,但后续的渲染可以修改映射点:

 //加入材质坐标数据
    const float txts[8] = {
            1.0f*0.95,0.0f , //右下
            0.0f,0.0f,
            1.0f*0.95,1.0f,
            0.0,1.0
    };

这样,在渲染的时候就自动裁掉了后面的边框。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
以下是一个使用Qt、FFmpegOpenGLWidget播放RTSP流的示例代码: 首先,确保已经安装了Qt和FFmpeg库,并在Qt项目中添加了相应的依赖项。 在Qt项目中创建一个自定义的OpenGLWidget类,用于显示视频帧: ```cpp // myopenglwidget.h #ifndef MYOPENGLWIDGET_H #define MYOPENGLWIDGET_H #include <QOpenGLWidget> #include <QOpenGLFunctions> #include <QOpenGLBuffer> #include <QOpenGLShaderProgram> #include <QOpenGLTexture> class MyOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions { Q_OBJECT public: explicit MyOpenGLWidget(QWidget *parent = nullptr); ~MyOpenGLWidget(); protected: void initializeGL() override; void resizeGL(int w, int h) override; void paintGL() override; private: QOpenGLBuffer m_vertexBuffer; QOpenGLShaderProgram m_shaderProgram; QOpenGLTexture m_texture; float m_vertices[12] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f }; }; #endif // MYOPENGLWIDGET_H ``` ```cpp // myopenglwidget.cpp #include "myopenglwidget.h" MyOpenGLWidget::MyOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent) { } MyOpenGLWidget::~MyOpenGLWidget() { } void MyOpenGLWidget::initializeGL() { initializeOpenGLFunctions(); m_vertexBuffer.create(); m_vertexBuffer.bind(); m_vertexBuffer.allocate(m_vertices, sizeof(m_vertices)); m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, "attribute vec3 aPosition;" "void main() {" " gl_Position = vec4(aPosition, 1.0);" "}"); m_shaderProgram.link(); m_shaderProgram.bind(); m_texture.create(); m_texture.setMinificationFilter(QOpenGLTexture::Nearest); m_texture.setMagnificationFilter(QOpenGLTexture::Linear); } void MyOpenGLWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); } void MyOpenGLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT); m_vertexBuffer.bind(); m_shaderProgram.bind(); int vertexLocation = m_shaderProgram.attributeLocation("aPosition"); m_shaderProgram.enableAttributeArray(vertexLocation); m_shaderProgram.setAttributeBuffer(vertexLocation, GL_FLOAT, 0, 3); glDrawArrays(GL_QUADS, 0, 4); } ``` 接下来,创建一个Qt窗口类,并在其中使用FFmpeg解码和播放RTSP流,并将帧渲染到OpenGLWidget中: ```cpp // mainwindow.h #ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> #include <QThread> #include <QTimer> #include <QImage> #include <QMutex> #include "myopenglwidget.h" extern "C" { #include <libavformat/avformat.h> #include <libswscale/swscale.h> } class VideoDecoder : public QThread { Q_OBJECT public: explicit VideoDecoder(QObject *parent = nullptr); ~VideoDecoder(); void setUrl(const QString &url); void stop(); signals: void frameDecoded(const QImage &image); protected: void run() override; private: QString m_url; bool m_stopRequested; QMutex m_mutex; void decodePacket(AVPacket *packet, AVCodecContext *codecContext, SwsContext *swsContext); }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void onFrameDecoded(const QImage &image); void onTimerTimeout(); private: MyOpenGLWidget *m_openglWidget; VideoDecoder *m_videoDecoder; QTimer *m_timer; }; #endif // MAINWINDOW_H ``` ```cpp // mainwindow.cpp #include "mainwindow.h" VideoDecoder::VideoDecoder(QObject *parent) : QThread(parent), m_stopRequested(false) { } VideoDecoder::~VideoDecoder() { stop(); } void VideoDecoder::setUrl(const QString &url) { m_url = url; } void VideoDecoder::stop() { QMutexLocker locker(&m_mutex); m_stopRequested = true; } void VideoDecoder::run() { av_register_all(); AVFormatContext *formatContext = nullptr; AVCodecContext *codecContext = nullptr; SwsContext *swsContext = nullptr; if (avformat_open_input(&formatContext, m_url.toUtf8().constData(), nullptr, nullptr) != 0) { qDebug() << "Failed to open input file"; return; } if (avformat_find_stream_info(formatContext, nullptr) < 0) { qDebug() << "Failed to find stream info"; avformat_close_input(&formatContext); return; } int videoStreamIndex = -1; for (unsigned int i = 0; i < formatContext->nb_streams; ++i) { if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) { videoStreamIndex = i; break; } } if (videoStreamIndex == -1) { qDebug() << "Failed to find video stream"; avformat_close_input(&formatContext); return; } AVCodec *codec = avcodec_find_decoder(formatContext->streams[videoStreamIndex]->codecpar->codec_id); if (!codec) { qDebug() << "Failed to find decoder"; avformat_close_input(&formatContext); return; } codecContext = avcodec_alloc_context3(codec); if (!codecContext) { qDebug() << "Failed to allocate codec context"; avformat_close_input(&formatContext); return; } if (avcodec_parameters_to_context(codecContext, formatContext->streams[videoStreamIndex]->codecpar) < 0) { qDebug() << "Failed to copy codec parameters to context"; avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return; } if (avcodec_open2(codecContext, codec, nullptr) < 0) { qDebug() << "Failed to open codec"; avcodec_free_context(&codecContext); avformat_close_input(&formatContext); return; } AVPacket *packet = av_packet_alloc(); AVFrame *frame = av_frame_alloc(); swsContext = sws_getContext(codecContext->width, codecContext->height, codecContext->pix_fmt, codecContext->width, codecContext->height, AV_PIX_FMT_RGB24, SWS_BILINEAR, nullptr, nullptr, nullptr); while (av_read_frame(formatContext, packet) >= 0) { if (m_stopRequested) break; if (packet->stream_index == videoStreamIndex) { decodePacket(packet, codecContext, swsContext); } av_packet_unref(packet); } av_packet_free(&packet); av_frame_free(&frame); avcodec_free_context(&codecContext); avformat_close_input(&formatContext); sws_freeContext(swsContext); } void VideoDecoder::decodePacket(AVPacket *packet, AVCodecContext *codecContext, SwsContext *swsContext) { AVFrame *frame = av_frame_alloc(); int ret = avcodec_send_packet(codecContext, packet); if (ret < 0) { qDebug() << "Error sending packet to decoder"; av_frame_free(&frame); return; } ret = avcodec_receive_frame(codecContext, frame); if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { av_frame_free(&frame); return; } else if (ret < 0) { qDebug() << "Error receiving frame from decoder"; av_frame_free(&frame); return; } QImage image(codecContext->width, codecContext->height, QImage::Format_RGB888); uint8_t *srcData[4] = { frame->data[0], frame->data[1], frame->data[2], nullptr }; int srcLinesize[4] = { frame->linesize[0], frame->linesize[1], frame->linesize[2], 0 }; uint8_t *dstData[1] = { image.bits() }; int dstLinesize[1] = { image.bytesPerLine() }; sws_scale(swsContext, srcData, srcLinesize, 0, codecContext->height, dstData, dstLinesize); emit frameDecoded(image); av_frame_free(&frame); } MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_openglWidget(new MyOpenGLWidget(this)), m_videoDecoder(new VideoDecoder(this)), m_timer(new QTimer(this)) { setCentralWidget(m_openglWidget); connect(m_videoDecoder, &VideoDecoder::frameDecoded, this, &MainWindow::onFrameDecoded); connect(m_timer, &QTimer::timeout, this, &MainWindow::onTimerTimeout); // 设置RTSP流的URL QString rtspUrl = "rtsp://example.com/stream"; m_videoDecoder->setUrl(rtspUrl); m_videoDecoder->start(); // 设置定时器来刷新OpenGLWidget int frameRate = 30; // 帧率 int timerInterval = 1000 / frameRate; m_timer->start(timerInterval); } MainWindow::~MainWindow() { m_videoDecoder->stop(); m_videoDecoder->wait(); } void MainWindow::onFrameDecoded(const QImage &image) { m_openglWidget->update(); // 触发OpenGLWidget的重绘事件 } void MainWindow::onTimerTimeout() { // 在OpenGLWidget的paintGL()函数中绘制当前帧 QMutexLocker locker(m_videoDecoder->getMutex()); QImage image = m_videoDecoder->getImage(); if (!image.isNull()) { // 将图像数据复制到OpenGLWidget中 // ... // 更新OpenGLWidget m_openglWidget->update(); } } ``` 这只是一个简单的示例,具体的实现可能会根据你的需求有所调整。你可以根据实际情况修改代码以适应你的应用程序。同时,你还需要根据Qt和FFmpeg的文档进行更详细的学习和了解。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值