一、前言
現在各個監控大廠做的設備,基本上都會支持通過rtsp直接取流顯示,而且做的比較好的還支持通過rtsp回放取流,基本上都會約定一個字符串的規則,每個廠家都是不一樣的規則,比如回放對應的rtsp地址還要帶上時間範圍,回放肯定要指定一個開始時間和結束時間。這裏需要特別提示的是,按道理rtsp是實時視頻流,一般是沒有時長的,而回放的rtsp視頻流是帶了時長的,所以可以通過seek來定位播放位置,這個就很方便用戶在軟件上任意拖動和切換播放位置,以前我一直以爲rtsp實時視頻流不可能有時長,原來是自己孤陋寡聞了,在通過一個老萬音視頻大佬的指點下才得知這個特性,這個特性當然需要設備廠家在後端實現支持。
有了回放可以切換播放進度位置這個特性,意味着回放這塊不需要用GB28181國標去解析,直接構建對應的回放視頻流字符串就可以,目前測試下來,正常播放和切換進度播放一點問題沒有,唯獨倍速播放有問題,目前看下來還是不支持倍速播放的,不知道是不是還有其他的機關要素控制比如參數啥的。其實取流回放的核心就是根據不同廠家拿到對應設備的rtsp字符串即可,解碼那邊要拿到時長,並當做文件處理,因爲文件類型的可以切換播放進度。
二、效果圖
三、體驗地址
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 體驗地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_demo。
四、功能特點
- 支持各種音視頻文件、本地攝像頭設備,各種視頻流網絡流。
- 支持開始播放、暫停播放、繼續播放、停止播放、設置播放進度、倍速播放。
- 可設置音量、靜音切換、抓拍圖片、錄像存儲。
- 自動提取專輯信息比如標題、藝術家、專輯、專輯封面,自動顯示專輯封面。
- 完美支持音視頻同步和倍速播放。
- 解碼策略支持速度優先、質量優先、均衡處理、最快速度。
- 支持手機視頻旋轉角度顯示,比如一般手機拍攝的視頻是旋轉了90度的,解碼顯示的時候需要重新旋轉90度纔是正的。
- 自動轉換yuv420格式,比如本地攝像頭是yuyv422格式,有些視頻文件是xx格式,統一將非yuv420格式轉換,然後再進行處理。
- 支持硬解碼dxva2、d3d11va等,性能極高尤其是大分辨率比如4K視頻。
- 視頻響應極低延遲0.2s左右,極速響應打開視頻流0.5s左右,專門做了優化處理。
- 硬解碼和GPU繪製組合,極低CPU佔用,比海康大華等客戶端更優。
- 支持視頻流中的各種音頻格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推薦選擇AAC兼容性跨平臺性最好。
- 視頻存儲支持yuv、h264、mp4多種格式,音頻存儲支持pcm、wav、aac多種格式。默認視頻mp4格式、音頻aac格式。
- 支持分開存儲音頻視頻文件,也支持合併到一個mp4文件,默認策略是無論何種音視頻文件格式存儲,最終都轉成mp4及aac格式,然後合併成音視頻一起的mp4文件。
- 支持本地攝像頭實時視頻顯示帶音頻輸入輸出,音視頻錄製合併到一個mp4文件。
- 支持H264/H265編碼(現在越來越多的監控攝像頭是H265視頻流格式)生成視頻文件,內部自動識別切換編碼格式。
- 自動識別視頻流動態分辨率改動,重新打開視頻流。
- 支持用戶信息中包含特殊字符(比如用戶信息中包含+#@等字符)的視頻流播放,內置解析轉義處理。
- 純qt+ffmpeg解碼,非sdl等第三方繪製播放依賴,gpu繪製採用qopenglwidget,音頻播放採用qaudiooutput。
- 同時支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5、ffmpeg6以及後續版本,全部做了兼容處理。如果需要支持xp需要選用ffmpeg3或ffmpeg2。
- 支持濾鏡,源頭帶各種水印及圖形效果,可以將OSD標籤信息和各種圖形信息寫入到MP4文件。
五、相關代碼
//地址參數結構體
struct UrlPara {
QString deviceIP; //通信地址
int devicePort; //通信端口
QString userName; //用戶名稱
QString userPwd; //用戶密碼
int channel; //通道編號
int streamType; //碼流類型
QString companyName; //廠家標識
CompanyType companyType; //廠家類型
int videoType; //視頻類型(0-實時/1-回放)
QDateTime dateTimeStart; //開始時間(回放專用)
QDateTime dateTimeEnd; //結束時間(回放專用)
UrlPara() {
devicePort = 0;
channel = 0;
streamType = 0;
}
//重載打印輸出格式
friend QDebug operator << (QDebug debug, const UrlPara &urlPara) {
QStringList list;
list << QString("通信地址: %1").arg(urlPara.deviceIP);
list << QString("通信端口: %1").arg(urlPara.devicePort);
list << QString("用戶名稱: %1").arg(urlPara.userName);
list << QString("用戶密碼: %1").arg(urlPara.userPwd);
list << QString("通道編號: %1").arg(urlPara.channel);
list << QString("碼流類型: %1").arg(urlPara.streamType);
list << QString("廠家標識: %1").arg(urlPara.companyName);
list << QString("廠家類型: %1").arg(urlPara.companyType);
#if (QT_VERSION >= QT_VERSION_CHECK(5,4,0))
debug.noquote() << list.join("\n");
#else
debug << list.join("\n");
#endif
return debug;
}
};
QString UrlHelper::getRtspUrl(const UrlPara &urlPara)
{
QString url;
//頭部地址格式完全一致
QString head = QString("rtsp://%1:%2@%3:554").arg(urlPara.userName).arg(urlPara.userPwd).arg(urlPara.deviceIP);
if (urlPara.companyType == CompanyType_HaiKang) {
//實時預覽格式 rtsp://admin:[email protected]:554/Streaming/Channels/101?transportmode=unicast
//視頻回放格式 rtsp://admin:[email protected]:554/Streaming/tracks/101?starttime=20120802t063812z&endtime=20120802t064816z
//流媒體視頻流 rtsp://172.6.24.15:554/Devicehc8://172.6.22.106:8000:0:0?username=admin&password=12345
//日期時間格式 ISO 8601 表示Zulu(GMT) 時間 YYYYMMDD”T”HHmmSS.fraction”Z”,
//unicast表示單播,multicast表示多播,默認單播可以省略
//101解析: 1是通道號 01是通道的碼流編號 也可以是02 03
QString startTimeISO = urlPara.dateTimeStart.toString(Qt::ISODate);
startTimeISO.replace("-", "");
startTimeISO.replace(":", "");
startTimeISO.toLower();
QString endTimeISO = urlPara.dateTimeEnd.toString(Qt::ISODate);
endTimeISO.replace("-", "");
endTimeISO.replace(":", "");
endTimeISO.toLower();
//通道號和碼流編號
QString info = QString("%1%2%3").arg(urlPara.channel).arg(0).arg(urlPara.streamType + 1);
//回放時間範圍
QString time = QString("starttime=%1z&endtime=%2z").arg(startTimeISO).arg(endTimeISO);
//實時和回放地址格式不同
if (urlPara.videoType == 0) {
url = QString("%1/Streaming/Channels/%2").arg(head).arg(info);
} else if (urlPara.videoType == 1) {
url = QString("%1/Streaming/tracks/%2?%3").arg(head).arg(info).arg(time);
}
} else if (urlPara.companyType == CompanyType_DaHua) {
//實時預覽格式 rtsp://192.168.1.128:554/cam/realmonitor?channel=1&subtype=0&unicast=true&proto=Onvif
//視頻回放格式 rtsp://admin:[email protected]:554/cam/playback?channel=1&subtype=0&starttime=2021_03_18_11_36_01&endtime=2021_03_18_12_05_01
QString startTimeStr = urlPara.dateTimeStart.toString("yyyy_MM_dd_HH_mm_ss");
QString endTimeStr = urlPara.dateTimeEnd.toString("yyyy_MM_dd_HH_mm_ss");
//通道號和碼流編號
QString info = QString("channel=%1&subtype=%2").arg(urlPara.channel).arg(urlPara.streamType);
//回放時間範圍
QString time = QString("starttime=%1&endtime=%2").arg(startTimeStr).arg(endTimeStr);
//實時和回放地址格式不同
if (urlPara.videoType == 0) {
url = QString("%1/cam/realmonitor?%2&unicast=true&proto=Onvif").arg(head).arg(info);
} else if (urlPara.videoType == 1) {
url = QString("%1/cam/playback?%2&%3").arg(head).arg(info).arg(time);
}
} else {
//實時預覽格式 rtsp://admin:[email protected]:554/live?channel=1&stream=1
//視頻回放格式 rtsp://admin:[email protected]:554/file?channel=1&start=1494485280&stop=1494485480
//先轉換時間戳,1970年到該時間經過的秒數
qint64 startTimeSec = urlPara.dateTimeStart.toMSecsSinceEpoch() / 1000;
qint64 stopTimeSec = urlPara.dateTimeEnd.toMSecsSinceEpoch() / 1000;
//回放時間範圍
QString time = QString("start=%1&stop=%2").arg(startTimeSec).arg(stopTimeSec);
//實時和回放地址格式不同
if (urlPara.videoType == 0) {
url = QString("%1/live?channel=%2&stream=%3").arg(head).arg(urlPara.channel).arg(urlPara.streamType);
} else if (urlPara.videoType == 1) {
url = QString("%1/file?channel=%2&%3").arg(head).arg(urlPara.channel).arg(time);
}
}
//還有一種通用格式 rtsp://admin:[email protected]:554/0 0-主碼流 1-子碼流
return url;
}