Qt編寫安防視頻監控系統14-本地回放


一、前言

在上一篇文章將視頻文件存儲好了,需要提供界面方便用戶查詢視頻文件進行回放,其實這個回放就是播放歷史存儲的視頻文件,並不是什么高大上的東西,視頻回放在這個系統中分三種,第一種是本地回放,回放存儲在客戶端本地的視頻文件;第二種是遠程回放,采用NVR廠家提供的SDK開發包或者國標GB28181協議來回放存儲在NVR上的視頻文件;第三種是設備回放(統一放在設備播放模塊),通過rtsp的格式來回放NVR上存儲的視頻文件,這個主要是方便第三方集成廠家開發,畢竟GB28181協議比較復雜,調試麻煩,不如直接指定特定的rtsp格式來回放視頻,當然需要在請求的rtsp的地址中帶上對應的用戶驗證信息,不然沒法保證安全性。

如果存儲采用的是ffmpeg,則回放該存儲的視頻文件,也是直接用ffmpeg打開文件播放即可,可以自由控制播放的速度,由於是裸流,默認很快的,在解碼的時候都是使勁的解碼繪制,所以這個特性剛好提供了速度控制的可能,自己在解碼的時候休息一下下,就相當於慢點播放。如果存儲采用的是vlc,則存儲的是標准的視頻文件,可以直接用其他播放器打開進行播放的,也可以用vlc打開本地文件播放的形式來播放。

本地回放模塊,提供了查詢界面,首先選擇某個通道或者所有通道,再選擇視頻類型,有存儲視頻和報警視頻兩種,一般這兩個都會分開文件夾存儲的,優先級不一樣,最后選擇開始時間和結束時間,默認是按照天來計算的,還可以精確到時分秒,選擇好以后單擊查詢按鈕則會將符合條件的所有視頻文件列出來,雙擊其中的一個文件就能啟用播放,一個視頻播放完畢,會自動跳到下一個文件繼續播放,直到最后一個文件,界面上提供了播放暫停按鈕,可以單擊暫停當前播放的視頻,還能看到播放的進度。

皮膚開源:https://gitee.com/feiyangqingyun/QWidgetDemo https://github.com/feiyangqingyun/QWidgetDemo
文件名稱:styledemo

體驗地址:https://gitee.com/feiyangqingyun/QWidgetExe https://github.com/feiyangqingyun/QWidgetExe
文件名稱:bin_video_system.zip

二、功能特點

  1. 支持16畫面切換,全屏切換等,包括1+4+6+8+9+13+16畫面切換。
  2. 支持alt+enter全屏,esc退出全屏。
  3. 自定義信息框+錯誤框+詢問框+右下角提示框。
  4. 17套皮膚樣式隨意更換,所有樣式全部統一,包括菜單等。
  5. 雲台儀表盤鼠標移上去高亮,八個方位精准識別。
  6. 底部畫面工具欄(畫面分割切換+截圖聲音等設置)移上去高亮。
  7. 可在配置文件更改左上角logo+中文軟件名稱+英文軟件名稱。
  8. 封裝了百度地圖,三維切換,設備點位,鼠標按下獲取經緯度等。
  9. 堆棧窗體,每個窗體都是個單獨的qwidget,方便編寫自己的代碼。
  10. 頂部鼠標右鍵菜單,可動態控制時間CPU+左上角面板+左下角面板+右上角面板+右下角面板的顯示和隱藏,支持恢復默認布局。
  11. 工具欄可以放置多個小圖標和關閉圖標。
  12. 左側右側可拖動拉伸,並自動記憶寬高位置,重啟后恢復。
  13. 雙擊攝像機節點自動播放視頻,雙擊節點自動依次添加視頻,會自動跳到下一個,雙擊父節點自動添加該節點下的所有視頻。
  14. 攝像機節點拖曳到對應窗體播放視頻,同時支持拖曳本地文件直接播放。
  15. 視頻畫面窗體支持拖曳交換,瞬間響應。
  16. 雙擊節點+拖曳節點+拖曳窗體交換位置,均自動更新url.txt。
  17. 支持從url.txt中加載16通道視頻播放,自動記憶最后通道對應的視頻,軟件啟動后自動打開播放。
  18. 右下角音量條控件,失去焦點自動隱藏,音量條帶靜音圖標。
  19. 集成百度地圖,可以添加設備對應位置,自動生成地圖,支持縮放和三維地圖,提供地圖風格選擇,共12種風格。
  20. 視頻拖動到通道窗體外自動刪除視頻。
  21. 鼠標右鍵可刪除當前+所有視頻,截圖當前+所有視頻。
  22. 錄像機管理、攝像機管理,可添加刪除修改導入導出打印信息,立即應用新的設備信息生成樹狀列表,不需重啟。
  23. 在pro文件中可以自由開啟是否加載地圖。
  24. 視頻播放可選四種內核自由切換,vlc+ffmpeg+easyplayer+海康sdk,均可在pro中設置。
  25. 可設置1+4+9+16畫面輪詢,可設置輪詢間隔以及輪詢碼流類型等,直接在主界面底部工具欄右側單擊啟動輪詢按鈕即可,再次單擊停止輪詢。
  26. 默認超過10秒鍾未操作自動隱藏鼠標指針。
  27. 支持onvif搜素設備,支持任意onvif攝像機,包括但不限於海康大華宇視天地偉業華為等,支持onvif雲台控制。
  28. 高度可定制化,用戶可以很方便的在此基礎上衍生自己的功能,支持linux系統。

三、效果圖

四、核心代碼

void frmVideoPlayLocal::on_btnSelect_clicked()
{
    QDate dateStart = ui->dateStart->date();
    QDate dateEnd = ui->dateEnd->date();
    if (dateStart > dateEnd) {
        QUIHelper::showMessageBoxError("開始時間不能大於結束時間!", 3);
        return;
    }

    //將日期轉換為日期時間計算相差的天數,超過60天則提示不用繼續
    QDateTime dateTimeStart = ui->dateStart->dateTime();
    QDateTime dateTimeEnd = ui->dateEnd->dateTime();
    if (dateTimeStart.daysTo(dateTimeEnd) >= 60) {
        QUIHelper::showMessageBoxError("每次最大只能查詢60天內!", 3);
        return;
    }

    ui->listWidget->clear();
    QString videoPath;
    if (ui->cboxType->currentText() == "存儲視頻") {
        videoPath = QString("%1/%2").arg(QUIHelper::appPath()).arg(DBData::VideoNormalPath);
    } else {
        videoPath = QString("%1/%2").arg(QUIHelper::appPath()).arg(DBData::VideoAlarmPath);
    }

    //獲取所有文件夾名稱,根據時間查詢對應通道對應類型視頻
    //如果開始時間小於或者等於結束時間,則將開始時間對應文件夾下的視頻文件添加到列表
    //然后將開始時間加一天,知道大於結束時間
    while (dateStart <= dateEnd) {
        QString savePath = QString("%1/%2").arg(videoPath).arg(dateStart.toString("yyyy-MM-dd"));
        QDir saveDir(savePath);
        QStringList filter;
        filter << "*.mp4" << "*.h264" ;
        QStringList files = saveDir.entryList(filter);

        foreach (QString file, files) {
            //如果是選擇的所有通道,則不過濾視頻文件
            if (ui->cboxCh->currentText() == "所有通道") {
                QListWidgetItem *item = new QListWidgetItem(ui->listWidget);
                item->setText(file);
                item->setData(Qt::UserRole, QString(savePath));
            } else {
                //對應通道的視頻文件添加進來
                QString videoCh = file.split("-").at(6).split(".").at(0);
                QString chName = QString("Ch%1").arg(ui->cboxCh->currentIndex());
                if (videoCh == chName) {
                    QListWidgetItem *item = new QListWidgetItem(ui->listWidget);
                    item->setText(file);
                    item->setData(Qt::UserRole, QString(savePath));
                }
            }
        }

        dateStart = dateStart.addDays(1);
    }

    ui->labTip->setText(QString("共找到 %1 個").arg(ui->listWidget->count()));
}

void frmVideoPlayLocal::on_btnPlayVideo_clicked()
{
    if (ui->listWidget->currentRow() < 0) {
        return;
    }

    bool check = ui->btnPlayVideo->isChecked();
    if (!check) {
        IconHelper::Instance()->setIcon(ui->btnPlayVideo, 0xf28d, btnRadius);
        widget->next();
        timerPlay->start();
    } else {
        IconHelper::Instance()->setIcon(ui->btnPlayVideo, 0xf144, btnRadius);
        widget->pause();
        timerPlay->stop();
    }
}

void frmVideoPlayLocal::on_listWidget_doubleClicked()
{
    timerPlay->stop();
    QListWidgetItem *item = ui->listWidget->currentItem();
    QString file = QString("%1/%2").arg(item->data(Qt::UserRole).toString()).arg(item->text());

    //將文件路徑轉為操作系統可以識別的路徑,在windows下Qt之外的程序調用路徑必須轉換
    file = QDir::toNativeSeparators(file);

    //存儲的視頻不能獲取視頻長度,這里另想高招,取文件的創建時間,用修改時間減去創建時間就是長度
    QFileInfo mp4File(file);
    QDateTime creatTime = mp4File.created();
    QDateTime lastTime = mp4File.lastModified();

    //如果創建時間大於最后修改時間,說明文件長度為0或者是復制過來的而不是正常的存儲文件.
    if (creatTime >= lastTime) {
        QUIHelper::showMessageBoxError("非正常存儲視頻文件,請重新選擇!");
        return;
    }

    //得到時間差,單位毫秒
    qint64 milliSecondTime = creatTime.msecsTo(lastTime);
    int hour = milliSecondTime / (60 * 60 * 1000);
    int minute = (milliSecondTime - hour * 60 * 60 * 1000) / (60 * 1000);
    int seconds = (milliSecondTime - hour * 60 * 60 * 1000 - minute * 60 * 1000) / 1000;

    if (seconds >= 60) {
        seconds = seconds % 60;
        minute += seconds / 60;
    }

    if (minute >= 60) {
        minute = minute % 60;
        hour  += minute / 60;
    }

    //播放時長重新計數
    hourPlay = 0;
    minutePlay = 0;
    secondsPlay = 0;
    secondsPlayCount = 0;
    timerPlay->start();

    //重新設置進度條和總時長
    currentVideoLenght = milliSecondTime / 1000;
    ui->slider->setMinimum(0);
    ui->slider->setMaximum(currentVideoLenght);
    ui->slider->setValue(0);
    ui->labTimeAll->setText(QString("總時長: %1時%2分%3秒").arg(hour).arg(minute).arg(seconds));

    //先釋放上一個播放的視頻,再打開當前播放的視頻
    widget->setUrl(file);
    widget->restart();

    currentVideo = file;
    ui->btnPlayVideo->setEnabled(true);
    ui->btnPlayVideo->setChecked(false);
    IconHelper::Instance()->setIcon(ui->btnPlayVideo, 0xf28d, btnRadius);
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM