Qt編寫監控實時顯示和取流回放工具(回放支持切換進度)

一、前言

現在各個監控大廠做的設備,基本上都會支持通過rtsp直接取流顯示,而且做的比較好的還支持通過rtsp回放取流,基本上都會約定一個字符串的規則,每個廠家都是不一樣的規則,比如回放對應的rtsp地址還要帶上時間範圍,回放肯定要指定一個開始時間和結束時間。這裏需要特別提示的是,按道理rtsp是實時視頻流,一般是沒有時長的,而回放的rtsp視頻流是帶了時長的,所以可以通過seek來定位播放位置,這個就很方便用戶在軟件上任意拖動和切換播放位置,以前我一直以爲rtsp實時視頻流不可能有時長,原來是自己孤陋寡聞了,在通過一個老萬音視頻大佬的指點下才得知這個特性,這個特性當然需要設備廠家在後端實現支持。

有了回放可以切換播放進度位置這個特性,意味着回放這塊不需要用GB28181國標去解析,直接構建對應的回放視頻流字符串就可以,目前測試下來,正常播放和切換進度播放一點問題沒有,唯獨倍速播放有問題,目前看下來還是不支持倍速播放的,不知道是不是還有其他的機關要素控制比如參數啥的。其實取流回放的核心就是根據不同廠家拿到對應設備的rtsp字符串即可,解碼那邊要拿到時長,並當做文件處理,因爲文件類型的可以切換播放進度。

二、效果圖

三、體驗地址

  1. 國內站點:https://gitee.com/feiyangqingyun
  2. 國際站點:https://github.com/feiyangqingyun
  3. 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 體驗地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_demo。

四、功能特點

  1. 支持各種音視頻文件、本地攝像頭設備,各種視頻流網絡流。
  2. 支持開始播放、暫停播放、繼續播放、停止播放、設置播放進度、倍速播放。
  3. 可設置音量、靜音切換、抓拍圖片、錄像存儲。
  4. 自動提取專輯信息比如標題、藝術家、專輯、專輯封面,自動顯示專輯封面。
  5. 完美支持音視頻同步和倍速播放。
  6. 解碼策略支持速度優先、質量優先、均衡處理、最快速度。
  7. 支持手機視頻旋轉角度顯示,比如一般手機拍攝的視頻是旋轉了90度的,解碼顯示的時候需要重新旋轉90度纔是正的。
  8. 自動轉換yuv420格式,比如本地攝像頭是yuyv422格式,有些視頻文件是xx格式,統一將非yuv420格式轉換,然後再進行處理。
  9. 支持硬解碼dxva2、d3d11va等,性能極高尤其是大分辨率比如4K視頻。
  10. 視頻響應極低延遲0.2s左右,極速響應打開視頻流0.5s左右,專門做了優化處理。
  11. 硬解碼和GPU繪製組合,極低CPU佔用,比海康大華等客戶端更優。
  12. 支持視頻流中的各種音頻格式,AAC、PCM、G.726、G.711A、G.711Mu、G.711ulaw、G.711alaw、MP2L2等都支持,推薦選擇AAC兼容性跨平臺性最好。
  13. 視頻存儲支持yuv、h264、mp4多種格式,音頻存儲支持pcm、wav、aac多種格式。默認視頻mp4格式、音頻aac格式。
  14. 支持分開存儲音頻視頻文件,也支持合併到一個mp4文件,默認策略是無論何種音視頻文件格式存儲,最終都轉成mp4及aac格式,然後合併成音視頻一起的mp4文件。
  15. 支持本地攝像頭實時視頻顯示帶音頻輸入輸出,音視頻錄製合併到一個mp4文件。
  16. 支持H264/H265編碼(現在越來越多的監控攝像頭是H265視頻流格式)生成視頻文件,內部自動識別切換編碼格式。
  17. 自動識別視頻流動態分辨率改動,重新打開視頻流。
  18. 支持用戶信息中包含特殊字符(比如用戶信息中包含+#@等字符)的視頻流播放,內置解析轉義處理。
  19. 純qt+ffmpeg解碼,非sdl等第三方繪製播放依賴,gpu繪製採用qopenglwidget,音頻播放採用qaudiooutput。
  20. 同時支持ffmpeg2、ffmpeg3、ffmpeg4、ffmpeg5、ffmpeg6以及後續版本,全部做了兼容處理。如果需要支持xp需要選用ffmpeg3或ffmpeg2。
  21. 支持濾鏡,源頭帶各種水印及圖形效果,可以將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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章