Qt使用ffmpeg获取视频文件封面图

10 篇文章 0 订阅

Qt使用ffmpeg获取视频文件封面图

#ifndef __THUM_HELPER_H_
#define __THUM_HELPER_H_

extern "C" {
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libswscale/swscale.h"
}
#include <QObject>
#include <QImage>
namespace Media {

	class ThumHelper :public QObject
	{
		Q_OBJECT
	public:
		ThumHelper();
		~ThumHelper();
		void initGlobal();
		QImage thumnail(const QString& videoPath);
	};
}

#endif  //__THUM_HELPER_H_
#include "ThumHelper.h"



Media::ThumHelper::ThumHelper()
{
	initGlobal();
}

void Media::ThumHelper::initGlobal()
{
	av_register_all();
}

QImage Media::ThumHelper::thumnail(const QString& videoPath)
{

	QImage image;
	AVFormatContext * fmtContext = nullptr;
	if (avformat_open_input(&fmtContext, videoPath.toStdString().c_str(), nullptr, nullptr) < 0) {
		return image;
	}
	if (avformat_find_stream_info(fmtContext, nullptr) < 0) {
		avformat_close_input(&fmtContext);
		return image;
	}
	int nStreamIndex = -1;
	AVCodecParameters *codecParameters = nullptr;
	for (int i = 0; i < fmtContext->nb_streams; i++) {
		if (fmtContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
			nStreamIndex = i;
			codecParameters = fmtContext->streams[i]->codecpar;
			break;
		}
	}
	if (nStreamIndex == -1) {
		avformat_close_input(&fmtContext);
		return image;
	}
	AVCodec* codec = avcodec_find_decoder(codecParameters->codec_id);

	if (!codec) {
		avformat_close_input(&fmtContext);
		return image;
	}
	AVCodecContext* codecContext = avcodec_alloc_context3(codec);
	if (!codecContext) {
		// 分配解码器上下文失败
		avformat_close_input(&fmtContext);
		return image;
	}
	if (avcodec_parameters_to_context(codecContext, codecParameters) < 0) {
		// 复制解码器参数到解码器上下文失败
		avcodec_free_context(&codecContext);
		avformat_close_input(&fmtContext);
		return image;
	}
	if (avcodec_open2(codecContext, codec, nullptr) < 0) {
		// 打开解码器失败
		avcodec_free_context(&codecContext);
		avformat_close_input(&fmtContext);
		return image;
	}
	AVPacket packet;
	av_init_packet(&packet);
	packet.data = nullptr;
	packet.size = 0;

	while (av_read_frame(fmtContext, &packet) >= 0) {
		if (packet.stream_index == nStreamIndex) {
			AVFrame* frame = av_frame_alloc();
			if (frame) {
				int ret = avcodec_send_packet(codecContext, &packet);
				if (ret >= 0) {
					ret = avcodec_receive_frame(codecContext, frame);
					if (ret >= 0) {
						// 将第一帧保存为封面图像
						if (frame->key_frame) {
							AVFrame* rgbFrame = av_frame_alloc();
							if (rgbFrame) {
								rgbFrame->format = AV_PIX_FMT_RGB24;
								rgbFrame->width = frame->width;
								rgbFrame->height = frame->height;

								int bufferSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, frame->width, frame->height, 1);
								uint8_t* buffer = new uint8_t[bufferSize];
								av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, frame->width, frame->height, 1);

								SwsContext* swsContext = sws_getContext(frame->width, frame->height, codecContext->pix_fmt,
									frame->width, frame->height, AV_PIX_FMT_RGB24, SWS_BICUBIC, nullptr, nullptr, nullptr);
								if (swsContext) {
									sws_scale(swsContext, frame->data, frame->linesize, 0, frame->height, rgbFrame->data, rgbFrame->linesize);
									sws_freeContext(swsContext);

									// 保存封面图像到文件
										int outputBufferSize = rgbFrame->width * rgbFrame->height * 3;
										uchar* outputBuffer = new uchar[outputBufferSize];

										for (int i = 0; i < rgbFrame->height; i++) {
											memcpy(outputBuffer + i * rgbFrame->width * 3, rgbFrame->data[0] + i * rgbFrame->linesize[0], rgbFrame->width * 3);
										}
										image = QImage(outputBuffer, rgbFrame->width, rgbFrame->height, QImage::Format_RGB888).copy();
										if (outputBuffer) {
											delete [] outputBuffer;
											outputBuffer = nullptr;
										}


									}
								
								if (buffer) {
									delete[] buffer;
									buffer = nullptr;
								}
								
								av_frame_free(&rgbFrame);
							}
						}
					}
				}

				av_frame_free(&frame);
			}
			break;
		}

		av_packet_unref(&packet);
	}

	avcodec_free_context(&codecContext);
	avformat_close_input(&fmtContext);
	return image;

}

Media::ThumHelper::~ThumHelper()
{

}

调用demo

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPushButton>
#include <QListWidget>

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = 0);
    ~Widget();
	


private:
	QPushButton* m_loadVideo = nullptr;
	QListWidget *m_imageList;
};

#endif // WIDGET_H

#include "widget.h"
#include "ThumHelper.h"
#include <QVBoxLayout>
#include <QFileDialog>
#include <QPixmap>
#include <QLabel>
#include <QDebug>
#include <QDateTime>
Widget::Widget(QWidget *parent)
    : QWidget(parent)
{
	QVBoxLayout  *layout = new QVBoxLayout;

	m_loadVideo = new QPushButton(this);
	m_loadVideo->setText("Load Image");
	m_imageList = new QListWidget;
	layout->addWidget(m_loadVideo);
	layout->addWidget(m_imageList);
	this->setLayout(layout);
	connect(m_loadVideo, &QPushButton::clicked, [&]() {
		QStringList paths = QFileDialog::getOpenFileNames(nullptr, QString("Open File"), "",tr("Videos(*.mp4 *.irgd)"));
		Media::ThumHelper  helper;
		QDateTime dateTime = QDateTime::currentDateTime();
		qDebug() << "Begin Load:" << QDateTime::currentDateTime().toString("yyyy-mm-DD-hh-MM-ss");

		for (int i = 0; i < paths.count(); i++) {
			QString videoPath = paths.at(i);
			
			QListWidgetItem*  item = new QListWidgetItem(m_imageList);
			QImage image = helper.thumnail(videoPath);
			item->setSizeHint(QSize(image.width() / 4, image.height() / 4));
			QLabel* label = new QLabel(m_imageList);
			label->setPixmap(QPixmap::fromImage(image).scaled(image.width()/4, image.height()/4));
			m_imageList->addItem(item);
			m_imageList->setItemWidget(item, label);
		}
		qDebug() << "finish Load:" << QDateTime::currentDateTime().toString("yyyy-mm-DD-hh-MM-ss");
	});
}

Widget::~Widget()
{

}

#-------------------------------------------------
#
# Project created by QtCreator 2023-12-21T13:13:56
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = Media
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which as been marked as deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if you use deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0


SOURCES += main.cpp\
        widget.cpp

HEADERS  += widget.h




INCLUDEPATH +=$$PWD/ffmpeg/4.2.1/include

message($$PWD/ffmpeg/4.2.1/include)

win32{
    CONFIG(debug,debug|release){
        LIBS+= -L$$PWD/ffmpeg/4.2.1/x64/lib
        message($$PWD/ffmpeg/4.2.1/x64/lib)

    }
}


LIBS+= -lavcodec \
       -lavfilter \
       -lavformat \
       -lswresample \
       -lswscale \
       -lpostproc \
       -lavutil \
       -lavdevice

  • 10
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Qt使用FFmpeg进行视频编码的步骤如下: 1. 引入FFmpeg库:在Qt项目中引入FFmpeg库,可以通过在.pro文件中添加相应的库路径和链接库来实现。 2. 初始化FFmpeg:在使用FFmpeg之前,需要进行初始化。可以通过调用`av_register_all()`函数来完成初始化。 3. 创建AVFormatContext:AVFormatContext是FFmpeg中的一个重要结构体,用于存储音视频封装格式的相关信息。可以通过调用`avformat_alloc_output_context2()`函数来创建AVFormatContext对象。 4. 打开输出文件使用`avio_open()`函数打开输出文件,并将文件指针赋值给AVFormatContext的pb字段。 5. 添加视频流:创建AVStream对象,并设置相关参数,如编码器、分辨率、帧率等。然后将AVStream对象添加到AVFormatContext中。 6. 打开视频编码器:使用`avcodec_find_encoder()`函数查找合适的视频编码器,并通过调用`avcodec_open2()`函数打开编码器。 7. 分配AVFrame:AVFrame用于存储视频帧的数据。可以通过调用`av_frame_alloc()`函数来分配AVFrame对象。 8. 编码视频帧:将原始的RGB数据转换为YUV格式,并将YUV数据填充到AVFrame中。然后调用`avcodec_send_frame()`函数将AVFrame发送给编码器进行编码。 9. 获取编码后的数据:使用`avcodec_receive_packet()`函数从编码器中获取编码后的数据。 10. 写入文件:将编码后的数据写入输出文件中,可以通过调用`av_write_frame()`函数来实现。 11. 释放资源:在完成视频编码后,需要释放所有的资源,包括AVFormatContext、AVStream、AVCodecContext、AVFrame等对象。 以下是一个使用QtFFmpeg进行视频编码的示例代码: ```cpp #include <QCoreApplication> #include <QDebug> #include <QImage> #include <QFile> #include <QBuffer> extern "C" { #include <libavformat/avformat.h> #include <libavcodec/avcodec.h> #include <libswscale/swscale.h> } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // 初始化FFmpeg av_register_all(); // 创建AVFormatContext AVFormatContext *formatContext = nullptr; avformat_alloc_output_context2(&formatContext, nullptr, nullptr, "output.mp4"); // 打开输出文件 QFile outputFile("output.mp4"); if (!outputFile.open(QIODevice::WriteOnly)) { qDebug() << "Failed to open output file"; return a.exec(); } AVIOContext *ioContext = nullptr; avio_open(&ioContext, outputFile.fileName().toUtf8().constData(), AVIO_FLAG_WRITE); formatContext->pb = ioContext; // 添加视频流 AVStream *videoStream = avformat_new_stream(formatContext, nullptr); if (!videoStream) { qDebug() << "Failed to create video stream"; return a.exec(); } // 设置视频流参数 AVCodecContext *codecContext = videoStream->codec; codecContext->codec_id = AV_CODEC_ID_H264; codecContext->codec_type = AVMEDIA_TYPE_VIDEO; codecContext->width = 640; codecContext->height = 480; codecContext->time_base = {1, 25}; codecContext->gop_size = 10; codecContext->pix_fmt = AV_PIX_FMT_YUV420P; // 打开视频编码器 AVCodec *codec = avcodec_find_encoder(codecContext->codec_id); if (!codec) { qDebug() << "Failed to find video encoder"; return a.exec(); } if (avcodec_open2(codecContext, codec, nullptr) < 0) { qDebug() << "Failed to open video encoder"; return a.exec(); } // 分配AVFrame AVFrame *frame = av_frame_alloc(); frame->format = codecContext->pix_fmt; frame->width = codecContext->width; frame->height = codecContext->height; av_frame_get_buffer(frame, 0); // 编码视频帧 AVPacket packet; av_init_packet(&packet); packet.data = nullptr; packet.size = 0; // 假设有一张RGB格式的片 QImage image("input.png"); if (image.isNull()) { qDebug() << "Failed to load input image"; return a.exec(); } QImage yuvImage = image.convertToFormat(QImage::Format_YUV420P); // 将YUV数据填充到AVFrame中 for (int y = 0; y < codecContext->height; y++) { memcpy(frame->data[0] + y * frame->linesize[0], yuvImage.constBits() + y * yuvImage.bytesPerLine(), frame->linesize[0]); } for (int y = 0; y < codecContext->height / 2; y++) { memcpy(frame->data[1] + y * frame->linesize[1], yuvImage.constBits() + yuvImage.bytesPerLine() * codecContext->height + y * yuvImage.bytesPerLine() / 2, frame->linesize[1]); memcpy(frame->data[2] + y * frame->linesize[2], yuvImage.constBits() + yuvImage.bytesPerLine() * codecContext->height * 5 / 4 + y * yuvImage.bytesPerLine() / 2, frame->linesize[2]); } // 发送AVFrame给编码器进行编码 avcodec_send_frame(codecContext, frame); // 获取编码后的数据 while (avcodec_receive_packet(codecContext, &packet) == 0) { // 写入文件 av_write_frame(formatContext, &packet); av_packet_unref(&packet); } // 写入文件尾部 av_write_trailer(formatContext); // 释放资源 av_frame_free(&frame); avcodec_close(codecContext); avio_close(formatContext->pb); avformat_free_context(formatContext); qDebug() << "Video encoding finished"; return a.exec(); } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值