1、包含必须的头文件。
#include <QMainWindow>
#include <QTimer>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
2、示例。
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private:
QTimer* m_timer;
QOpenGLWidget* m_glWidget;
AVFormatContext* m_formatCtx;
AVCodecContext* m_codecCtx;
AVFrame* m_frame;
QOpenGLShaderProgram m_shaderProgram;
QOpenGLBuffer m_vertexBuffer;
void initializeFFmpeg();
void initializeOpenGL();
void updateFrame();
private slots:
void onTimeout();
};
#endif // MAINWINDOW_H
// mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
m_glWidget = new QOpenGLWidget(this);
setCentralWidget(m_glWidget);
m_timer = new QTimer(this);
connect(m_timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
m_timer->start(30); // 30ms刷新一次
initializeFFmpeg();
initializeOpenGL();
}
MainWindow::~MainWindow()
{
av_frame_free(&m_frame);
avcodec_close(m_codecCtx);
avformat_close_input(&m_formatCtx);
avformat_free_context(m_formatCtx);
}
void MainWindow::initializeFFmpeg()
{
m_formatCtx = avformat_alloc_context();
if (avformat_open_input(&m_formatCtx, "path_to_video_file", nullptr, nullptr) != 0) {
qDebug() << "Failed to open video file";
return;
}
if (avformat_find_stream_info(m_formatCtx, nullptr) < 0) {
qDebug() << "Failed to find stream information";
return;
}
int videoStreamIndex = -1;
for (unsigned int i = 0; i < m_formatCtx->nb_streams; i++) {
if (m_formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStreamIndex = i;
break;
}
}
if (videoStreamIndex == -1) {
qDebug() << "Failed to find video stream";
return;
}
AVCodecParameters* codecPar = m_formatCtx->streams[videoStreamIndex]->codecpar;
AVCodec* codec = avcodec_find_decoder(codecPar->codec_id);
if (!codec) {
qDebug() << "Failed to find decoder";
return;
}
m_codecCtx = avcodec_alloc_context3(codec);
if (!m_codecCtx) {
qDebug() << "Failed to allocate codec context";
return;
}
if (avcodec_parameters_to_context(m_codecCtx, codecPar) < 0) {
qDebug() << "Failed to copy codec parameters to codec context";
return;
}
if (avcodec_open2(m_codecCtx, codec, nullptr) != 0) {
qDebug() << "Failed to open codec";
return;
}
m_frame = av_frame_alloc();
if (!m_frame) {
qDebug() << "Failed to allocate frame";
return;
}
}
void MainWindow::initializeOpenGL()
{
m_glWidget->makeCurrent();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glEnable(GL_TEXTURE_2D);
// 初始化纹理
GLuint textureId;
f->glGenTextures(1, &textureId);
f->glBindTexture(GL_TEXTURE_2D, textureId);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
f->glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载着色器程序
m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute vec4 vertexIn;"
"attribute vec2 textureIn;"
"varying vec2 textureOut;"
"void main(void) {"
" gl_Position = vertexIn;"
" textureOut = textureIn;"
"}");
m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,
"uniform sampler2D textureYUV;"
"varying vec2 textureOut";"
"void main(void) {"
" gl_FragColor = texture2D(textureYUV, textureOut);"
"}");
m_shaderProgram.link();
// 创建顶点缓冲区
GLfloat vertices[] = {
-1.0f, 1.0f,
1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, -1.0f
};
m_vertexBuffer.create();
m_vertexBuffer.bind();
m_vertexBuffer.allocate(vertices, sizeof(vertices));
m_vertexBuffer.release();
}
void MainWindow::updateFrame()
{
AVPacket pkt;
av_init_packet(&pkt);
pkt.data = nullptr;
pkt.size = 0;
while (av_read_frame(m_formatCtx, &pkt) >= 0) {
if (pkt.stream_index == m_videoStreamIndex) {
int ret = avcodec_send_packet(m_codecCtx, &pkt);
if (ret < 0) {
qDebug() << "Error sending packet to decoder";
break;
}
while (ret >= 0) {
ret = avcodec_receive_frame(m_codecCtx, m_frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
break;
else if (ret < 0) {
qDebug() << "Error during decoding";
break;
}
// 渲染图像到OpenGL窗口
m_glWidget->makeCurrent();
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glBindTexture(GL_TEXTURE_2D, textureId);
f->glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_frame->width, m_frame->height,
GL_BGR, GL_UNSIGNED_BYTE, m_frame->data[0]);
m_glWidget->doneCurrent();
av_frame_unref(m_frame);
}
}
av_packet_unref(&pkt);
}
void MainWindow::onTimeout()
{
updateFrame();
m_glWidget->update();
}
// main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
采用OpenGL渲染图像,相比传统的QImage效率着实要高很多。