一、環境介紹
作業系統: win10 64位
QT版本: QT5.12.6
編譯器: MinGW 32
ffmpeg版本: 4.2.2
二、功能介紹
使用QT+ffmpeg設計的流媒體播放器,實時播放RTMP、RTSP視訊流渲染顯示。
測試大華攝像頭、海康攝像頭、CCTV直播頻道等視訊均可正常播放,實測延遲時間在1秒以内。
部分工程代碼截圖:
軟體運作效果:
CCVT的RTMP流拉取效果:
大華攝像頭的RTMP流拉取效果:
大華攝像頭的RTSP流拉取效果:
C++音視訊配套學習資料:點選莬費領取→音視訊開發(資料文檔+視訊教程+面試題)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
三、RTMP與RTSP協定介紹
RTMP
RTMP是Real Time Messaging Protocol(實時消息傳輸協定)的首字母縮寫。該協定基于TCP,是一個協定族,包括RTMP基本協定及RTMPT/RTMPS/RTMPE等多種變種。RTMP是一種設計用來進行實時資料通信的網絡協定,主要用來在Flash/AIR平台和支援RTMP協定的流媒體/互動伺服器之間進行音視訊和資料通信。支援該協定的軟體包括Adobe Media Server/Ultrant Media Server/red5等。RTMP與HTTP一樣,都屬于TCP/IP四層模型的應用層。
RTMP又是Routing Table Maintenance Protocol(路由選擇表維護協定)的縮寫。 在 AppleTalk 協定組中,路由選擇表維護協定(RTMP,Routing Table Maintenance Protocol)是一種傳輸層協定,它在 AppleTalk 路由器中建立并維護路由選擇表。RTMP 基于路由選擇資訊協定(RIP)。正如 RIP 一樣,RTMP 使用跳數作為路由計量标準。一個資料包從源 網絡發送到目标網絡,必須通過的路由器或其它中間媒體節點數目的計算結果即為跳數。
RTSP
RTSP(Real Time Streaming Protocol),RFC2326,實時流傳輸協定,是TCP/IP協定體系中的一個應用層協定,由哥倫比亞大學、網景和RealNetworks公司送出的IETF RFC标準。該協定定義了一對多應用程式如何有效地通過IP網絡傳送多媒體資料。RTSP在體系結構上位于RTP和RTCP之上,它使用TCP或UDP完成資料傳輸。HTTP與RTSP相比,HTTP請求由客戶機發出,伺服器作出響應;使用RTSP時,客戶機和伺服器都可以送出請求,即RTSP可以是雙向的。RTSP是用來控制聲音或影像的多媒體串流協定,并允許同時多個串流需求控制,傳輸時所用的網絡通訊協定并不在其定義的範圍内,伺服器端可以自行選擇使用TCP或UDP來傳送串流内容,它的文法和運作跟HTTP 1.1類似,但并不特别強調時間同步,是以比較能容忍網絡延遲。而前面提到的允許同時多個串流需求控制(Multicast),除了可以降低伺服器端的網絡用量,更進而支援多方視訊會議(Video Conference)。因為與HTTP1.1的運作方式相似,是以代理伺服器〈Proxy〉的快取功能〈Cache〉也同樣适用于RTSP,并因RTSP具有重新導向功能,可視實際負載情況來轉換提供服務的伺服器,以避免過大的負載集中于同一伺服器而造成延遲。
四、FFMPEG介紹
FFmpeg是一套可以用來記錄、轉換數字音頻、視訊,并能将其轉化為流的開源計算機程式。采用LGPL或GPL許可證。它提供了錄制、轉換以及流化音視訊的完整解決方案。它包含了非常先進的音頻/視訊編解碼庫libavcodec,為了保證高可移植性和編解碼品質,libavcodec裡很多code都是從頭開發的。
多媒體視訊處理工具FFmpeg有非常強大的功能包括視訊采集功能、視訊格式轉換、視訊抓圖、給視訊加水印等。
FFmpeg在Linux平台下開發,但它同樣也可以在其它作業系統環境中編譯運作,包括Windows、Mac OS X等。這個項目最早由Fabrice Bellard發起,2004年至2015年間由Michael Niedermayer主要負責維護。許多FFmpeg的開發人員都來自MPlayer項目,而且目前FFmpeg也是放在MPlayer項目組的伺服器上。項目的名稱來自MPEG視訊編碼标準,前面的"FF"代表"Fast Forward"。FFmpeg編碼庫可以使用GPU加速。
【相關學習資料推薦,點選下方連結免費報名,先碼住不迷路~】
【免費】FFmpeg/WebRTC/RTMP/NDK/Android音視訊流媒體進階開發-學習視訊教程-騰訊課堂
C++音視訊配套學習資料:點選莬費領取→音視訊開發(資料文檔+視訊教程+面試題)(FFmpeg+WebRTC+RTMP+RTSP+HLS+RTP)
五、核心代碼
5.1 xxx.pro :MinGW配置方式
QT += core gui
QT += multimediawidgets
QT += xml
QT += multimedia
QT += network
QT += widgets
QT += serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked 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 it uses 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 += \
Thread_FFMPEG_LaLiu.cpp \
main.cpp \
videoplayer_showvideowidget.cpp \
widget.cpp
HEADERS += \
Thread_FFMPEG_LaLiu.h \
videoplayer_showvideowidget.h \
widget.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/${TARGET}/bin
else: unix:!android: target.path = /opt/${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
win32
{
message('運作win32版本')
INCLUDEPATH+=C:/FFMPEG/ffmpeg_x86_4.2.2/include
LIBS+=C:/FFMPEG/ffmpeg_x86_4.2.2/bin/av*
LIBS+=C:/FFMPEG/ffmpeg_x86_4.2.2/bin/sw*
LIBS+=C:/FFMPEG/ffmpeg_x86_4.2.2/bin/pos*
}
RESOURCES += \
image.qrc
RC_ICONS=main.ico
5.2 xxx.pro: VS編譯器配置方式
QT += core gui
QT += multimediawidgets
QT += xml
QT += multimedia
QT += network
QT += widgets
QT += serialport
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked 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 it uses 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 += \
Thread_FFMPEG_LaLiu.cpp \
main.cpp \
videoplayer_showvideowidget.cpp \
widget.cpp
HEADERS += \
Thread_FFMPEG_LaLiu.h \
videoplayer_showvideowidget.h \
widget.h
FORMS += \
widget.ui
# Default rules for deployment.
qnx: target.path = /tmp/${TARGET}/bin
else: unix:!android: target.path = /opt/${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
win32
{
message('運作win32版本')
INCLUDEPATH+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/include
LIBS+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/lib/avcodec.lib
LIBS+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/lib/avformat.lib
LIBS+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/lib/avfilter.lib
LIBS+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/lib/avutil.lib
LIBS+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/lib/swresample.lib
LIBS+=C:/FFMPEG/ffmpeg_x86_x64_3.3.2/lib/swscale.lib
}
RESOURCES += \
image.qrc
RC_ICONS=main.ico
5.3 widget.h
#ifndef WIDGET_H
#define WIDGET_H
#pragma execution_character_set("utf-8")
#include <QWidget>
#include <QCompleter>
#include "Thread_FFMPEG_LaLiu.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
//主線程
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void SetStyle(const QString &qssFile);
void Log_Text_Display(QPlainTextEdit *plainTextEdit_log,QString text);
private slots:
void Log_Display(QString text);
void VideoDataDisplay(QImage image);
void on_pushButton_start_clicked();
void on_pushButton_stop_play_clicked();
void on_pushButton_no_display_clicked();
private:
Ui::Widget *ui;
bool log_widge_state=true;
};
#endif // WIDGET_H
5.4 widget.cpp
#include "widget.h"
#include "ui_widget.h"
/*
* 設定QT界面的樣式
*/
void Widget::SetStyle(const QString &qssFile) {
QFile file(qssFile);
if (file.open(QFile::ReadOnly)) {
QString qss = QLatin1String(file.readAll());
qApp->setStyleSheet(qss);
QString PaletteColor = qss.mid(20,7);
qApp->setPalette(QPalette(QColor(PaletteColor)));
file.close();
}
else
{
qApp->setStyleSheet("");
}
}
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
/*基本設定*/
// this->SetStyle(":/images/blue.css"); //設定樣式表
this->setWindowIcon(QIcon(":/main.ico")); //設定圖示
this->setWindowTitle("流媒體播放器");
//建構預設位址
QStringList listyear;
listyear<<"rtmp://10.0.0.13:8888/live/video"
<<"rtmp://58.200.131.2:1935/livetv/cctv14"
<<"rtsp://admin:[email protected]:554/cam/realmonitor?channel=1&subtype=0"
; //清單
QCompleter *year = new QCompleter(listyear);//建構自動補全器
ui->lineEdit_rtmp_url->setCompleter(year); //設定自動補全器功能
//連接配接拉流線程的圖像輸出信号
connect(&thread_laliu,SIGNAL(VideoDataOutput(QImage )),this,SLOT(VideoDataDisplay(QImage )));
//連接配接拉流線程的日志資訊
connect(&thread_laliu,SIGNAL(LogSend(QString)),this,SLOT(Log_Display(QString)));
}
Widget::~Widget()
{
delete ui;
}
//視訊重新整理顯示
void Widget::VideoDataDisplay(QImage image)
{
ui->widget_display->slotGetOneFrame(image);
}
/*日志顯示*/
void Widget::Log_Text_Display(QPlainTextEdit *plainTextEdit_log,QString text)
{
plainTextEdit_log->insertPlainText(text);
//移動滾動條到底部
QScrollBar *scrollbar = plainTextEdit_log->verticalScrollBar();
if(scrollbar)
{
scrollbar->setSliderPosition(scrollbar->maximum());
}
}
//日志顯示
void Widget::Log_Display(QString text)
{
Log_Text_Display(ui->plainTextEdit_log,text);
}
//開始拉流
void Widget::on_pushButton_start_clicked()
{
on_pushButton_stop_play_clicked();
//設定位址
thread_laliu.SetRTMPAddr(ui->lineEdit_rtmp_url->text());
//開始運作線程
thread_laliu.start();
}
/*
工程: ffmpeg_Laliu
日期: 2021-07-30
作者: DS小龍哥
環境: win10 QT5.12.6 MinGW32
功能: 停止播放
*/
void Widget::on_pushButton_stop_play_clicked()
{
if(thread_laliu.isRunning())
{
thread_laliu.Exit_process();
thread_laliu.quit();
thread_laliu.wait();
}
}
/*
工程: ffmpeg_Laliu
日期: 2021-07-30
作者: DS小龍哥
環境: win10 QT5.12.6 MinGW32
功能: 隐藏日志視窗
*/
void Widget::on_pushButton_no_display_clicked()
{
log_widge_state=!log_widge_state;
ui->groupBox->setVisible(log_widge_state);
}
5.5 widget渲染視窗--渲染視訊畫面
#include "videoplayer_showvideowidget.h"
#include <QPainter>
VideoPlayer_ShowVideoWidget::VideoPlayer_ShowVideoWidget(QWidget *parent) :
QWidget(parent)
{
m_nRotateDegree=0;
this->setMouseTracking(true);
}
VideoPlayer_ShowVideoWidget::~VideoPlayer_ShowVideoWidget()
{
}
void VideoPlayer_ShowVideoWidget::Set_Rotate(int Rotate)
{
m_nRotateDegree=Rotate;
}
void VideoPlayer_ShowVideoWidget::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.setRenderHint(QPainter::TextAntialiasing);
painter.setRenderHint(QPainter::SmoothPixmapTransform);
painter.setRenderHint(QPainter::HighQualityAntialiasing);
painter.setBrush(Qt::black);
painter.drawRect(0,0,this->width(),this->height()); //先畫成黑色
if (mImage.size().width() <= 0) return;
//将圖像按比例縮放成和視窗一樣大小
QImage img = mImage.scaled(this->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation); //這裡效率比較低下 還不知道如何優化
//畫面旋轉
if(m_nRotateDegree > 0)
{
QTransform matrix;
matrix.rotate(m_nRotateDegree);
img = img.transformed(matrix, Qt::SmoothTransformation);
}
int x = this->width() - img.width();
int y = this->height() - img.height();
x /= 2;
y /= 2;
painter.drawImage(QPoint(x,y),img); //畫出圖像
}
void VideoPlayer_ShowVideoWidget::slotGetOneFrame(QImage img)
{
src_mImage =mImage = img;
update(); //調用update将執行 paintEvent函數
}
/*
工程: QtAV_VideoPlayer
日期: 2021-03-24
作者: DS小龍哥
環境: win10 QT5.12.6 MinGW32
功能: 擷取原圖資料
*/
QImage VideoPlayer_ShowVideoWidget::GetImage()
{
return src_mImage.copy();
}
/*
工程: QtAV_VideoPlayer
日期: 2021-03-25
作者: DS小龍哥
環境: win10 QT5.12.6 MinGW32
功能: 滑鼠輕按兩下事件
*/
void VideoPlayer_ShowVideoWidget::mouseDoubleClickEvent(QMouseEvent *e)
{
emit s_VideoWidgetEvent(1);
}
如果你對音視訊開發感興趣,覺得文章對您有幫助,别忘了點贊、收藏哦!或者對本文的一些闡述有自己的看法,有任何問題,歡迎在下方評論區與我讨論!