天天看點

qt designer設定頂級布局後混亂_Qt編寫的RTSP播放器+視訊監控(ffmpeg版本)一、前言二、實作的功能三、效果圖四、核心代碼五、其他說明

一、前言

記得四年前就寫了個簡易版本的,當時寫得非常粗糙,也比較吃記憶體,代碼實在慘不忍睹,時隔多年後,重新寫了個版本,同時還解決了以前不支援1畫面+4畫面+6畫面+8畫面+13畫面+16畫面切換等異形布局的問題。這次直接将所有的處理全部封裝到一個類中,這樣用起來就很友善,直接引入一個頭檔案即可,而且整個控件繼承自qwidget,也可以界面上放一個widget滑鼠右鍵提升為即可。

二、實作的功能

* 1:多線程實時播放rtsp視訊流

* 2:支援X86和嵌入式linux

* 3:多線程顯示圖像,不卡主界面

* 4:自動重連網絡攝像頭

* 5:可設定邊框大小即偏移量和邊框顔色

* 6:可設定是否繪制OSD标簽即标簽文本或圖檔和标簽位置

* 7:可設定兩種OSD位置和風格

* 8:可設定是否儲存到檔案以及檔案名

* 9:可設定間隔時間段儲存檔案到指定目錄

* 10:可播放本地視訊檔案,支援設定幀率

* 11:支援h265視訊流+rtmp等常見視訊流

* 12:可暫停播放和繼續播放

* 13:支援定時存儲檔案,包括音頻和視訊

* 14:支援sdl播放音頻

* 15:支援外部拖曳檔案+拖曳節點資料進行播放

* 16:自定義頂部懸浮條,發送單擊信号通知,可設定是否啟用

* 17:支援qsv dxva d3d 硬解碼

三、效果圖

qt designer設定頂級布局後混亂_Qt編寫的RTSP播放器+視訊監控(ffmpeg版本)一、前言二、實作的功能三、效果圖四、核心代碼五、其他說明
qt designer設定頂級布局後混亂_Qt編寫的RTSP播放器+視訊監控(ffmpeg版本)一、前言二、實作的功能三、效果圖四、核心代碼五、其他說明
qt designer設定頂級布局後混亂_Qt編寫的RTSP播放器+視訊監控(ffmpeg版本)一、前言二、實作的功能三、效果圖四、核心代碼五、其他說明
qt designer設定頂級布局後混亂_Qt編寫的RTSP播放器+視訊監控(ffmpeg版本)一、前言二、實作的功能三、效果圖四、核心代碼五、其他說明

四、核心代碼

void FFmpegWidget::resizeEvent(QResizeEvent *){ //重新設定頂部工具欄的位置和寬高,可以自行設定頂部顯示或者底部顯示 int height = 20; flowPanel->setGeometry(borderWidth, borderWidth, this->width() - (borderWidth * 2), height); //flowPanel->setGeometry(borderWidth, this->height() - height - borderWidth, this->width() - (borderWidth * 2), height);}void FFmpegWidget::enterEvent(QEvent *){ //這裡還可以增加一個判斷,是否擷取了焦點的才需要顯示 //if (this->hasFocus()) {} if (flowEnable) { flowPanel->setVisible(true); }}void FFmpegWidget::leaveEvent(QEvent *){ if (flowEnable) { flowPanel->setVisible(false); }}void FFmpegWidget::dropEvent(QDropEvent *event){ //拖放完畢滑鼠松開的時候執行 //判斷拖放進來的類型,取出檔案,進行播放 if(event->mimeData()->hasUrls()) { QString url = event->mimeData()->urls().first().toLocalFile(); this->close(); this->setUrl(url); this->open(); emit fileDrag(url); } else if (event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) { QTreeWidget *treeWidget = (QTreeWidget *)event->source(); if (treeWidget != 0) { QString url = treeWidget->currentItem()->data(0, Qt::UserRole).toString(); this->close(); this->setUrl(url); this->open(); emit fileDrag(url); } }}void FFmpegWidget::dragEnterEvent(QDragEnterEvent *event){ //拖曳進來的時候先判斷下類型,非法類型則不處理 if(event->mimeData()->hasFormat("application/x-qabstractitemmodeldatalist")) { event->setDropAction(Qt::CopyAction); event->accept(); } else if(event->mimeData()->hasFormat("text/uri-list")) { event->setDropAction(Qt::LinkAction); event->accept(); } else { event->ignore(); }}void FFmpegWidget::paintEvent(QPaintEvent *){ //如果不需要繪制 if (!drawImage) { return; } //qDebug() << TIMEMS << "paintEvent" << objectName(); QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing); //繪制邊框 if (borderWidth > 0) { drawBorder(&painter); } if (!image.isNull()) { //繪制背景圖檔 drawImg(&painter, image); //繪制标簽1 if (osd1Visible) { drawOSD(&painter, osd1FontSize, osd1Text, osd1Color, osd1Image, osd1Format, osd1Position); } //繪制标簽2 if (osd2Visible) { drawOSD(&painter, osd2FontSize, osd2Text, osd2Color, osd2Image, osd2Format, osd2Position); } } else { //繪制背景 drawBg(&painter); }}void FFmpegWidget::drawBorder(QPainter *painter){ painter->save(); QPen pen; pen.setWidth(borderWidth); pen.setColor(hasFocus() ? focusColor : borderColor); painter->setPen(pen); painter->drawRect(rect()); painter->restore();}void FFmpegWidget::drawBg(QPainter *painter){ painter->save(); //背景圖檔為空則繪制文字,否則繪制背景圖檔 if (bgImage.isNull()) { painter->setPen(palette().foreground().color()); painter->drawText(rect(), Qt::AlignCenter, bgText); } else { //居中繪制 int pixX = rect().center().x() - bgImage.width() / 2; int pixY = rect().center().y() - bgImage.height() / 2; QPoint point(pixX, pixY); painter->drawImage(point, bgImage); } painter->restore();}void FFmpegWidget::drawImg(QPainter *painter, QImage img){ painter->save(); int offset = borderWidth * 1 + 0; img = img.scaled(width() - offset, height() - offset, Qt::KeepAspectRatio, Qt::SmoothTransformation); if (fillImage) { QRect rect(offset / 2, offset / 2, width() - offset, height() - offset); painter->drawImage(rect, img); } else { //按照比例自動居中繪制 int pixX = rect().center().x() - img.width() / 2; int pixY = rect().center().y() - img.height() / 2; QPoint point(pixX, pixY); painter->drawImage(point, img); } painter->restore();}void FFmpegWidget::drawOSD(QPainter *painter, int osdFontSize, const QString &osdText, const QColor &osdColor, const QImage &osdImage, const FFmpegWidget::OSDFormat &osdFormat, const FFmpegWidget::OSDPosition &osdPosition){ painter->save(); //标簽位置盡量偏移多一點避免遮擋 QRect osdRect(rect().x() + (borderWidth * 2), rect().y() + (borderWidth * 2), width() - (borderWidth * 5), height() - (borderWidth * 5)); int flag = Qt::AlignLeft | Qt::AlignTop; QPoint point = QPoint(osdRect.x(), osdRect.y()); if (osdPosition == OSDPosition_Left_Top) { flag = Qt::AlignLeft | Qt::AlignTop; point = QPoint(osdRect.x(), osdRect.y()); } else if (osdPosition == OSDPosition_Left_Bottom) { flag = Qt::AlignLeft | Qt::AlignBottom; point = QPoint(osdRect.x(), osdRect.height() - osdImage.height()); } else if (osdPosition == OSDPosition_Right_Top) { flag = Qt::AlignRight | Qt::AlignTop; point = QPoint(osdRect.width() - osdImage.width(), osdRect.y()); } else if (osdPosition == OSDPosition_Right_Bottom) { flag = Qt::AlignRight | Qt::AlignBottom; point = QPoint(osdRect.width() - osdImage.width(), osdRect.height() - osdImage.height()); } if (osdFormat == OSDFormat_Image) { painter->drawImage(point, osdImage); } else { QDateTime now = QDateTime::currentDateTime(); QString text = osdText; if (osdFormat == OSDFormat_Date) { text = now.toString("yyyy-MM-dd"); } else if (osdFormat == OSDFormat_Time) { text = now.toString("HH:mm:ss"); } else if (osdFormat == OSDFormat_DateTime) { text = now.toString("yyyy-MM-dd HH:mm:ss"); } //設定顔色及字号 QFont font; font.setPixelSize(osdFontSize); painter->setPen(osdColor); painter->setFont(font); painter->drawText(osdRect, flag, text); } painter->restore();}QImage FFmpegWidget::getImage() const{ return this->image;}QDateTime FFmpegWidget::getLastTime() const{ return ffmpeg->getLastTime();}QString FFmpegWidget::getUrl() const{ return ffmpeg->getUrl();}bool FFmpegWidget::getCopyImage() const{ return this->copyImage;}bool FFmpegWidget::getCheckLive() const{ return this->checkLive;}bool FFmpegWidget::getDrawImage() const{ return this->drawImage;}bool FFmpegWidget::getFillImage() const{ return this->fillImage;}bool FFmpegWidget::getFlowEnable() const{ return this->flowEnable;}int FFmpegWidget::getTimeout() const{ return this->timeout;}int FFmpegWidget::getBorderWidth() const{ return this->borderWidth;}QColor FFmpegWidget::getBorderColor() const{ return this->borderColor;}QColor FFmpegWidget::getFocusColor() const{ return this->focusColor;}QString FFmpegWidget::getBgText() const{ return this->bgText;}QImage FFmpegWidget::getBgImage() const{ return this->bgImage;}bool FFmpegWidget::getOSD1Visible() const{ return this->osd1Visible;}int FFmpegWidget::getOSD1FontSize() const{ return this->osd1FontSize;}QString FFmpegWidget::getOSD1Text() const{ return this->osd1Text;}QColor FFmpegWidget::getOSD1Color() const{ return this->osd1Color;}QImage FFmpegWidget::getOSD1Image() const{ return this->osd1Image;}FFmpegWidget::OSDFormat FFmpegWidget::getOSD1Format() const{ return this->osd1Format;}FFmpegWidget::OSDPosition FFmpegWidget::getOSD1Position() const{ return this->osd1Position;}bool FFmpegWidget::getOSD2Visible() const{ return this->osd2Visible;}int FFmpegWidget::getOSD2FontSize() const{ return this->osd2FontSize;}QString FFmpegWidget::getOSD2Text() const{ return this->osd2Text;}QColor FFmpegWidget::getOSD2Color() const{ return this->osd2Color;}QImage FFmpegWidget::getOSD2Image() const{ return this->osd2Image;}FFmpegWidget::OSDFormat FFmpegWidget::getOSD2Format() const{ return this->osd2Format;}FFmpegWidget::OSDPosition FFmpegWidget::getOSD2Position() const{ return this->osd2Position;}void FFmpegWidget::updateImage(const QImage &image){ //拷貝圖檔有個好處,當處理器比較差的時候,圖檔不會産生斷層,缺點是占用時間 //預設QImage類型是淺拷貝,可能正在繪制的時候,那邊已經更改了圖檔的上部分資料 this->image = copyImage ? image.copy() : image; this->update();}void FFmpegWidget::checkVideo(){ QDateTime now = QDateTime::currentDateTime(); QDateTime lastTime = ffmpeg->getLastTime(); int sec = lastTime.secsTo(now); if (sec >= timeout) { restart(); }}void FFmpegWidget::btnClicked(){ QPushButton *btn = (QPushButton *)sender(); emit btnClicked(btn->objectName());}void FFmpegWidget::setInterval(int interval){ ffmpeg->setInterval(interval);}void FFmpegWidget::setSleepTime(int sleepTime){ ffmpeg->setSleepTime(sleepTime);}void FFmpegWidget::setCheckTime(int checkTime){ ffmpeg->setCheckTime(checkTime);}void FFmpegWidget::setCheckConn(bool checkConn){ ffmpeg->setCheckConn(checkConn);}void FFmpegWidget::setUrl(const QString &url){ ffmpeg->setUrl(url);}void FFmpegWidget::setHardware(const QString &hardware){ ffmpeg->setHardware(hardware);}void FFmpegWidget::setSaveFile(bool saveFile){ ffmpeg->setSaveFile(saveFile);}void FFmpegWidget::setSaveInterval(int saveInterval){ ffmpeg->setSaveInterval(saveInterval);}void FFmpegWidget::setSavePath(const QString &savePath){ //如果目錄不存在則建立 QDir dir(savePath); if (!dir.exists()) { dir.mkdir(savePath); } ffmpeg->setSavePath(savePath);}void FFmpegWidget::setFileName(const QString &fileName){ ffmpeg->setFileName(fileName);}void FFmpegWidget::setCopyImage(bool copyImage){ this->copyImage = copyImage;}void FFmpegWidget::setCheckLive(bool checkLive){ this->checkLive = checkLive;}void FFmpegWidget::setDrawImage(bool drawImage){ this->drawImage = drawImage;}void FFmpegWidget::setFillImage(bool fillImage){ this->fillImage = fillImage;}void FFmpegWidget::setFlowEnable(bool flowEnable){ this->flowEnable = flowEnable;}void FFmpegWidget::setTimeout(int timeout){ this->timeout = timeout;}void FFmpegWidget::setBorderWidth(int borderWidth){ this->borderWidth = borderWidth;}void FFmpegWidget::setBorderColor(const QColor &borderColor){ this->borderColor = borderColor;}void FFmpegWidget::setFocusColor(const QColor &focusColor){ this->focusColor = focusColor;}void FFmpegWidget::setBgText(const QString &bgText){ this->bgText = bgText;}void FFmpegWidget::setBgImage(const QImage &bgImage){ this->bgImage = bgImage;}void FFmpegWidget::setOSD1Visible(bool osdVisible){ this->osd1Visible = osdVisible;}void FFmpegWidget::setOSD1FontSize(int osdFontSize){ this->osd1FontSize = osdFontSize;}void FFmpegWidget::setOSD1Text(const QString &osdText){ this->osd1Text = osdText;}void FFmpegWidget::setOSD1Color(const QColor &osdColor){ this->osd1Color = osdColor;}void FFmpegWidget::setOSD1Image(const QImage &osdImage){ this->osd1Image = osdImage;}void FFmpegWidget::setOSD1Format(const FFmpegWidget::OSDFormat &osdFormat){ this->osd1Format = osdFormat;}void FFmpegWidget::setOSD1Position(const FFmpegWidget::OSDPosition &osdPosition){ this->osd1Position = osdPosition;}void FFmpegWidget::setOSD2Visible(bool osdVisible){ this->osd2Visible = osdVisible;}void FFmpegWidget::setOSD2FontSize(int osdFontSize){ this->osd2FontSize = osdFontSize;}void FFmpegWidget::setOSD2Text(const QString &osdText){ this->osd2Text = osdText;}void FFmpegWidget::setOSD2Color(const QColor &osdColor){ this->osd2Color = osdColor;}void FFmpegWidget::setOSD2Image(const QImage &osdImage){ this->osd2Image = osdImage;}void FFmpegWidget::setOSD2Format(const FFmpegWidget::OSDFormat &osdFormat){ this->osd2Format = osdFormat;}void FFmpegWidget::setOSD2Position(const FFmpegWidget::OSDPosition &osdPosition){ this->osd2Position = osdPosition;}void FFmpegWidget::open(){ //qDebug() << TIMEMS << "open video" << objectName(); clear(); //如果是圖檔則隻顯示圖檔就行 image = QImage(ffmpeg->getUrl()); if (!image.isNull()) { update(); return; } ffmpeg->play(); ffmpeg->start(); if (checkLive) { timerCheck->start(); }}void FFmpegWidget::pause(){ ffmpeg->pause();}void FFmpegWidget::next(){ ffmpeg->next();}void FFmpegWidget::close(){ //qDebug() << TIMEMS << "close video" << objectName(); if (ffmpeg->isRunning()) { ffmpeg->stop(); ffmpeg->quit(); ffmpeg->wait(500); } if (checkLive) { timerCheck->stop(); } QTimer::singleShot(1, this, SLOT(clear()));}void FFmpegWidget::restart(){ //qDebug() << TIMEMS << "restart video" << objectName(); close(); QTimer::singleShot(10, this, SLOT(open()));}void FFmpegWidget::clear(){ image = QImage(); update();}
           

五、其他說明

1:可以用過目錄下的rtsp.txt設定要顯示的視訊流位址。

2:支援4畫面+6畫面+8畫面+9畫面+13畫面+16畫面切換。

3:支援輕按兩下最大化目前畫面。

4:支援右鍵截圖。

5:支援各種視訊流和本地檔案播放。

6:可設定OSD标簽。

7:在所有可能測試的作業系統和平台上均編譯通過并完美運作。

8:XP運作報錯的話請執行目錄下的fixff.cmd即可。

繼續閱讀