使用Qt5和OpenCV做視訊播放器
阿新 • • 發佈:2019-02-07
宣告在前頭:本文是查閱整合了眾多網友的知識的基礎上實現的。
首先,說在前頭。通過實現了使用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,更專業些。