Qt 通过ffmpeg获取视频预览图(windows下)

一、 搭建ffmpeg开发环境

1. 下载ffmpeg shared库

很早之前用过一段时间的ffmpeg,主要参考的雷神的博客,现在回过头搭建下开发环境,发现都不一样了,这里记录下环境搭建过程。

官网:https://www.ffmpeg.org/
github地址:https://github.com/FFmpeg/FFmpeg

打开官网:
在这里插入图片描述
点击Download,本文选择windows下的 Windows builds from gyan.dev
在这里插入图片描述
然后选择release builds下面的 ffmpeg-release-full-shared.7z下载

在这里插入图片描述
下载完成后,解压拿到下面目录结构文件:
在这里插入图片描述

2. 设置环境变量

然后在电脑->环境变量中加入bin目录
在这里插入图片描述

二、qtCreator 集成ffmpeg

新建工程,在pro文件中加入:

QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

INCLUDEPATH += "C:/Qt/ffmpeg-5.0-full_build-shared/include"
LIBS += -LC:/Qt/ffmpeg-5.0-full_build-shared/lib -lavutil -lavformat -lavcodec -lavfilter -lswscale -lswresample

注意自己的库路径

代码中加入

extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}

三、实现视频预览,完整demo如下

在线地址和本地文件均可

附上在线测试地址:http://www.w3school.com.cn/example/html5/mov_bbb.mp4
main.cpp

#include "mainwindow.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    return a.exec();
}

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    void createPreviewWidthFile(const char * file);
private:
    Ui::MainWindow *ui;


};
#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QtDebug>
#include <QImage>
#include <QElapsedTimer>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libswresample/swresample.h>
}

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

    //本地文件
    //createPreviewWidthFile("C:/Users/wmm/Desktop/demo/test.mp4");
    //在线地址
    QElapsedTimer t;
    t.start();
    createPreviewWidthFile("http://www.w3school.com.cn/example/html5/mov_bbb.mp4");
    qDebug()<< "last = "<< t.elapsed();
}

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

void MainWindow::createPreviewWidthFile(const char *file){
    AVFormatContext* fmt_ctx_ = nullptr;

    qDebug() << "111" << avcodec_version();

    //打开视频文件
    int errCode = avformat_open_input(&fmt_ctx_, file, nullptr, nullptr);
    if(errCode != 0){
        qDebug() << "avformat_open_input fail" << errCode;
        return;
    }
    qDebug() << "222" ;
    //读取音视频流信息
    errCode = avformat_find_stream_info(fmt_ctx_, nullptr);
    if(errCode != 0){
        qDebug() << "avformat_find_stream_info fail" << errCode;
        avformat_close_input(&fmt_ctx_);
        return;
    }
    qDebug() << "333" ;
    //打印输出视频相关信息
    av_dump_format(fmt_ctx_, 0, file, 0);
    qDebug() << "444" ;

    AVPacket* pkt = av_packet_alloc();
    AVFrame* temp_frame = av_frame_alloc();
    SwsContext* sws_ctx = nullptr;
    int ret = 0;
    QImage preview;
    bool preview_done = false;

    int videoStream = 0;
    for (int i=0; i<int(fmt_ctx_->nb_streams) && !preview_done; i++){
        //只处理视频信息
        if (fmt_ctx_->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            //查找视频解码器
            videoStream = i;
            const AVCodec* codec = avcodec_find_decoder(fmt_ctx_->streams[i]->codecpar->codec_id);
            AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);
            //根据提供的编解码器参数的值填充编解码器上下文
            avcodec_parameters_to_context(codec_ctx, fmt_ctx_->streams[i]->codecpar);
            //打开解码器
            avcodec_open2(codec_ctx, codec, nullptr);
            qDebug() << "555" ;
            //读取帧数据
            while (av_read_frame(fmt_ctx_, pkt) >= 0){
                if (pkt->stream_index == videoStream) {

                    av_frame_unref(temp_frame);
                    qDebug() << "666" ;
                    //对视频帧数据进行解码
                    while ((ret = avcodec_receive_frame(codec_ctx, temp_frame)) == AVERROR(EAGAIN)){
                        ret = avcodec_send_packet(codec_ctx, pkt);
                        qDebug() << "777" ;
                        if (ret < 0) {
                            qCritical() << "Failed to send packet to decoder." << ret;
                            break;
                        }
                    }

                    if(ret < 0 && ret != AVERROR_EOF){
                        qDebug() << "Failed to receive packet from decoder." << ret;
                        continue;
                    }

                    qDebug() << "777" << temp_frame->width << temp_frame->height;
                    //等比例缩放
                    int dstH = 240;
                    int dstW = qRound(dstH * (float(temp_frame->width)/float(temp_frame->height)));
                    //消除可能的告警
                    dstH = (dstH >> 4) << 4;
                    dstW = (dstW >> 4) << 4;

                    qDebug() << "777" << dstW << dstH;

                    sws_ctx = sws_getContext(
                                temp_frame->width,
                                temp_frame->height,
                                static_cast<AVPixelFormat>(temp_frame->format),
                                dstW,
                                dstH,
                                static_cast<AVPixelFormat>(AV_PIX_FMT_RGBA),
                                SWS_FAST_BILINEAR,
                                nullptr,
                                nullptr,
                                nullptr
                                );
                    int linesize[AV_NUM_DATA_POINTERS];
                    linesize[0] = dstW*4;
                    qDebug() << "888" ;
                    //生成图片
                    preview = QImage(dstW, dstH, QImage::Format_RGBA8888);
                    uint8_t* data = preview.bits();
                    sws_scale(sws_ctx,
                              temp_frame->data,
                              temp_frame->linesize,
                              0,
                              temp_frame->height,
                              &data,
                              linesize);
                    sws_freeContext(sws_ctx);

                    qDebug() << "9999" ;
                    avcodec_close(codec_ctx);
                    avcodec_free_context(&codec_ctx);
                    preview_done = true;
                    break;
                }
            }
        }
    }

    qDebug() << "10" ;
    av_frame_free(&temp_frame);
    av_packet_free(&pkt);
    avformat_close_input(&fmt_ctx_);
    if(preview_done){
        ui->label->setPixmap(QPixmap::fromImage(preview));
    }
}


运行效果图如下:
在这里插入图片描述

刚开始报错 FFmpeg: avcodec_send_packet() -1094995529

解决方法
调用 int av_read_frame(AVFormatContext *s, AVPacket *pkt) 函数 时,将会读取一帧数据并填充到AVPacket里面,但是读取的这帧数据可能是视频也可能是音频,也可能是字幕,具体是什么类型可以通过 AVPacket对象的 stream_index 属性来判断,因此在读取完一帧数据后,调用 avcodec_send_packet(inputCodecCtx, inputPacket) 函数将这帧数据发送到解码队列之前,一定要确保这帧数据(AVPacket)的类型与 解码器上下文的类型保持一致!

也就是说你AVPacket中的stream_index对应的是音频流数据,那你在调用avcodec_send_packet()函数时,第二个参数传入的必须是一个音频解码器上下文,只有这样send packet才有可能成功。

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值