Qt显示FFmpeg解码的图片
  TTGEfHowA3iM 2023年11月02日 43 0


一、前言

之前讲过通过FFmpeg解码视频,将视频帧保存为图片,这次我们来将解码的图片播放起来;

思路:

  • 首先,解码还是十步走战略,但是这个不能放在主线程里,耗时操作会卡住界面,所以我们将解码操作放入子线程中运行;
  • 其次,子线程无法直接修改主界面UI,所以通过信号槽将子线程解码的QImage传递到主界面显示;
  • 最后,主界面将传递过来的图片实时更新到QLabel上即可;

二、效果展示

Qt显示FFmpeg解码的图片


三、详细代码

解码线程类

#ifndef VIDEOPLAYER_H
#define VIDEOPLAYER_H

extern "C"{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"
}

#include <stdio.h>

#include <QThread>
#include <QImage>
#include <QDebug>

class VideoPlayer : public QThread
{
Q_OBJECT
public:
VideoPlayer();

protected:
void run();

signals:
void sig_post_oneFrame(const QImage& image);
};

#endif // VIDEOPLAYER_H
#include "videoplayer.h"

VideoPlayer::VideoPlayer()
{

}

void VideoPlayer::run()
{
//变量定义
//==================================================================================
char *file_path = "C:/Users/wangjichuan/Desktop/2.mp4"; //文件路径

///=====第一步=====
//初始化FFMPEG 调用了这个才能正常使用编码器和解码器
av_register_all();
///===================================================================

///=====第二步=====
AVFormatContext *pFormatCtx; //描述了一个媒体文件或媒体流的构成和基本信息
pFormatCtx = avformat_alloc_context(); //分配一个解封装上下文指针

//打开文件
if (avformat_open_input(&pFormatCtx, file_path, NULL, NULL) != 0) { //文件信息存储到文件上下文中,后续对它进行处理即可
printf("无法打开文件");
return;
}
///===================================================================

///=====第三步=====
//探寻文件中是否存在信息流
if (avformat_find_stream_info(pFormatCtx, NULL) < 0) {
printf("文件中没有发现信息流");
return;
}

//循环查找视频中包含的流信息,直到找到视频类型的流
//便将其记录下来 保存到videoStream变量中
//这里我们现在只处理视频流 音频流先不管他
int videoStream = -1;
int i;
for (i = 0; i < pFormatCtx->nb_streams; i++) {
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
videoStream = i;
}
}

//如果videoStream为-1 说明没有找到视频流
if (videoStream == -1) {
printf("文件中未发现视频流");
return;
}
///===================================================================

///=====第四步=====
AVCodecContext *pCodecCtx; //描述编解码器上下文的数据结构,包含了众多编解码器需要的参数信息
AVCodec *pCodec; //存储编解码器信息的结构体

//查找解码器
pCodecCtx = pFormatCtx->streams[videoStream]->codec; //获取视频流中编码器上下文
pCodec = avcodec_find_decoder(pCodecCtx->codec_id); //获取视频流的编码器

if (pCodec == NULL) {
printf("未发现编码器");
return;
}
///===================================================================

///=====第五步=====
//打开解码器
//==================================================================================
if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0) {
printf("无法打开编码器");
return;
}
///===================================================================

///=====第六步=====
static struct SwsContext *img_convert_ctx; //用于视频图像的转换
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height,
pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
AV_PIX_FMT_BGR32, SWS_BICUBIC, NULL, NULL, NULL);
///===================================================================

///=====第七步=====
int numBytes;
numBytes = avpicture_get_size(AV_PIX_FMT_BGR32, pCodecCtx->width,pCodecCtx->height);
///===================================================================

///=====第八步=====
AVFrame *pFrame, *pFrameRGB; //存储音视频原始数据(即未被编码的数据)的结构体
pFrame = av_frame_alloc();
pFrameRGB = av_frame_alloc();

uint8_t *out_buffer; //缓存
out_buffer = (uint8_t *) av_malloc(numBytes * sizeof(uint8_t));
avpicture_fill((AVPicture *) pFrameRGB, out_buffer, AV_PIX_FMT_BGR32,
pCodecCtx->width, pCodecCtx->height);
///===================================================================

///=====第九步=====
AVPacket *packet; //保存了解复用(demuxer)之后,解码(decode)之前的数据(仍然是压缩后的数据)和关于这些数据的一些附加的信息
int ret, got_picture;

int y_size = pCodecCtx->width * pCodecCtx->height;
packet = (AVPacket *) malloc(sizeof(AVPacket)); //分配一个packet
av_new_packet(packet, y_size); //分配packet的数据

av_dump_format(pFormatCtx, 0, file_path, 0); //输出视频信息

while (1)
{
if (av_read_frame(pFormatCtx, packet) < 0) {
break; //这里认为视频读取完了
}

if (packet->stream_index == videoStream) {
ret = avcodec_decode_video2(pCodecCtx, pFrame, &got_picture,packet);

if (ret < 0) {
printf("decode error.");
return;
}

if (got_picture) {
sws_scale(img_convert_ctx,
(uint8_t const * const *) pFrame->data,
pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
pFrameRGB->linesize);

QImage image(pFrameRGB->data[0], pCodecCtx->width, pCodecCtx->height, pFrameRGB->linesize[0], QImage::Format_RGB32);
emit sig_post_oneFrame(image);
}
}
av_free_packet(packet);
}
///===================================================================

///=====第十步=====
av_free(out_buffer);
av_free(pFrameRGB);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
///===================================================================
}

主窗体类

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QDebug>
#include <QLabel>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

#include "videoplayer.h"

class MainWindow : public QMainWindow
{
Q_OBJECT

public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();

QLabel* label;
VideoPlayer* m_player;

public slots:
void slot_get_oneFrame(const QImage& image);


private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);

label = new QLabel(this);
this->setCentralWidget(label);

m_player = new VideoPlayer;
connect(m_player,SIGNAL(sig_post_oneFrame(QImage)),this,SLOT(slot_get_oneFrame(QImage)));
m_player->start();
}

MainWindow::~MainWindow()
{
delete ui;
}

void MainWindow::slot_get_oneFrame(const QImage& image)
{
label->setPixmap(QPixmap::fromImage(image));
}


【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
  b1UHV4WKBb2S   2023年11月13日   39   0   0 ide抗锯齿
  HE3leaVn7jMN   2023年11月24日   29   0   0 Timei++#include
  HE3leaVn7jMN   2023年11月26日   29   0   0 i++#include
  b1UHV4WKBb2S   2023年11月13日   33   0   0 裁剪ideflutter
  zSWNgACtCQuP   2023年11月13日   29   0   0 ide
TTGEfHowA3iM