1. 程式人生 > >使用Qt5和OpenCV做視訊播放器

使用Qt5和OpenCV做視訊播放器

宣告在前頭:本文是查閱整合了眾多網友的知識的基礎上實現的。

首先,說在前頭。通過實現了使用OpenCV做播放器的過程,才發現OpenCV真的不適合做播放器,至少是沒有什麼太多視訊處理理論基礎認識的初學者。實現過程中也是歷盡千辛萬苦,查閱很多資料。

使用環境是Qt5.7.0+VS2013+OpenCV2.4.13。在這裡就不說環境搭建了,如果會使用動態庫,這些步驟應該不是問題。但是寫本文的原因是如何使用程式碼去實現。

廢話不多說,直接上程式碼。

首先看標頭檔案 OpenCVDemo3.h

#ifndef OPENCVDEMO3_H
#define OPENCVDEMO3_H


#include <QtWidgets/QWidget>
#include "ui_OpenCVDemo3.h"
//opencv標頭檔案引用
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <QTimer>
#include <QImage>
using namespace cv;
class OpenCVDemo3 : public QWidget
{
	Q_OBJECT


public:
	OpenCVDemo3(QWidget *parent = 0);
	~OpenCVDemo3();
	QImage  MatToQImage(const cv::Mat& mat);
private slots:
	void onBtnClicked();
	void onTimeout();
	void onBtnFastClicked();
	void onBtnSlowClicked();
private:
	Ui::OpenCVDemo3Class ui;
	VideoCapture capture; //用來讀取視訊結構
	QTimer timer;//視訊播放的定時器
	int beishu;//調節播放速率
	int delay;//幀延遲時間
};


#endif // OPENCVDEMO3_H

下面是原始檔OpenCVDemo3.cpp
#include "OpenCVDemo3.h"
#include <QDebug>
OpenCVDemo3::OpenCVDemo3(QWidget *parent)
	: QWidget(parent)
	, beishu(1)
	,delay(0)
{
	ui.setupUi(this);
	connect(ui.pushButton, SIGNAL(clicked()), this, SLOT(onBtnClicked()));
	connect(ui.pushButton_fast, SIGNAL(clicked()), this, SLOT(onBtnFastClicked()));
	connect(ui.pushButton_slow, SIGNAL(clicked()), this, SLOT(onBtnSlowClicked()));
	connect(&timer, SIGNAL(timeout()), this, SLOT(onTimeout()));
}

OpenCVDemo3::~OpenCVDemo3()
{
	capture.release();
}
void OpenCVDemo3::onBtnFastClicked()
{

	beishu = beishu * 2;
	timer.stop();
	timer.start(delay / beishu);
}
void OpenCVDemo3::onBtnSlowClicked()
{
	beishu = beishu / 2;
	timer.stop();
	timer.start(delay / beishu);
}
void OpenCVDemo3::onBtnClicked()
{

	//開啟視訊檔案:其實就是建立一個VideoCapture結構
	capture.open("F:\\win32\\openCVDemo1\\OpenCVDemo2\\2.mp4");
	//檢測是否正常開啟:成功開啟時,isOpened返回ture
	if (!capture.isOpened())
		ui.textEdit->append("fail to open!");
	//獲取整個幀數
	long totalFrameNumber = capture.get(CV_CAP_PROP_FRAME_COUNT);
	ui.textEdit->append(QString::fromLocal8Bit("整個視訊共 %1 幀").arg(totalFrameNumber));

	ui.label->resize(QSize(capture.get(CV_CAP_PROP_FRAME_WIDTH), capture.get(CV_CAP_PROP_FRAME_HEIGHT)));
	//設定開始幀()
	long frameToStart = 0;
	capture.set(CV_CAP_PROP_POS_FRAMES, frameToStart);
	ui.textEdit->append(QString::fromLocal8Bit("從第 %1 幀開始讀").arg(frameToStart));

	//獲取幀率
	double rate = capture.get(CV_CAP_PROP_FPS);
	ui.textEdit->append(QString::fromLocal8Bit("幀率為:第 %1 ").arg(rate));

	delay = 1000 / rate;
	timer.start(delay / beishu);

}
void OpenCVDemo3::onTimeout()
{
	Mat frame;
	//讀取下一幀
	if (!capture.read(frame))
	{
		ui.textEdit->append(QString::fromLocal8Bit("讀取視訊失敗"));
		return;
	}

	QImage image=MatToQImage(frame);
	
	ui.label->setScaledContents(true);
	QSize qs = ui.label->rect().size();
	ui.label->setPixmap(QPixmap::fromImage(image).scaled(qs));
	
	ui.label->repaint();
	//這裡加濾波程式

	long totalFrameNumber = capture.get(CV_CAP_PROP_POS_FRAMES);

	ui.textEdit->append(QString::fromLocal8Bit("正在讀取第:第 %1 幀").arg(totalFrameNumber));


}
QImage OpenCVDemo3::MatToQImage(const cv::Mat& mat)
{
	// 8-bits unsigned, NO. OF CHANNELS = 1    
	if (mat.type() == CV_8UC1)
	{
		QImage image(mat.cols, mat.rows, QImage::Format_Indexed8);
		// Set the color table (used to translate colour indexes to qRgb values)    
		image.setColorCount(256);
		for (int i = 0; i < 256; i++)
		{
			image.setColor(i, qRgb(i, i, i));
		}
		// Copy input Mat    
		uchar *pSrc = mat.data;
		for (int row = 0; row < mat.rows; row++)
		{
			uchar *pDest = image.scanLine(row);
			memcpy(pDest, pSrc, mat.cols);
			pSrc += mat.step;
		}
		return image;
	}
	// 8-bits unsigned, NO. OF CHANNELS = 3    
	else if (mat.type() == CV_8UC3)
	{
		// Copy input Mat   
		const uchar *pSrc = (const uchar*)mat.data;
		// Create QImage with same dimensions as input Mat    
		QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_RGB888);
		return image.rgbSwapped();
	}
	else if (mat.type() == CV_8UC4)
	{
		// Copy input Mat    
		const uchar *pSrc = (const uchar*)mat.data;
		// Create QImage with same dimensions as input Mat    
		QImage image(pSrc, mat.cols, mat.rows, mat.step, QImage::Format_ARGB32);
		return image.copy();
	}
	else
	{
		return QImage();
	}
}
最後執行的效果如圖所示:

總結在最後:

首先使用OpenCV做播放視訊的例項不少,很多網友都提供了原始碼。但是往往大多數都是使用OpenCV自己的播放視窗,然後使用一個while迴圈來做。但是真正的使OpenCV和Qt結合在一起的大多是攝像頭的程式碼。所以本文在整合網友資源的同時,加入了自己的想法,來實現播放本地視訊。

然後,曾嘗試使用多執行緒來解決播放問題,但是在嘗試的過程中,發現不可行。所以果斷放棄了。使用定時器是網上的一貫用法。但是有一些視訊的fps資訊和幀數資訊不準確時,卻會出現問題。

最後,本程式碼只是實現了視訊的播放,但是視訊中的音訊資訊卻無法播放。所以這是不推薦使用它做播放器的原因。所以做視訊播放器還是推薦FFMpeg和VLC,更專業些。