One, foreword

Previously, there are three modules: local playback, remote playback and device playback. Local playback is used to play back the video files stored on the software local computer. Remote playback requires video playback from NVR by calling manufacturer SDK or GB28181 (not implemented, consideration will be added later); Device playback Through the general RTSP rules call the NVR in the form of video streams to store video or real-time video playback; Recently added a picture playback module, is used to play back the image file in the picture folder, the queue drawing picture sequence, looks a little video feeling.

Have one scenario is that the user’s own artificial intelligence algorithm, to be able to get a bunch of image sequence, the custom rules are stored, and trigger the alarm after got a pile of sequence images, so that you can play through this module in image sequences directly, also support the import and export, playback speed can be control, pull the playback progress bar located directly to the corresponding image sequence.

Image storage Rules

  • The default home directory is image_normal.
  • The home directory is stored according to the date directory, such as 2021-04-07 2021-04-08.
  • Under the date directory is a single image file such as ch1_2021-04-07-14-08-11-222.jpg.
  • The extension function can store corresponding data files such as warning text and picture files. The extension name can be TXT as well as a directory name.

Two, functions and features

(1) Software module

  1. Video surveillance module, various docking small form submodules, including equipment list, graphic alarm, window information, PTC control, preset position, cruise setting, equipment control, suspension map, web browsing, etc.
  2. Video playback module, including local playback, remote playback, device playback, picture playback, and video upload.
  3. Electronic map module, including picture map, online map, offline map, path planning and so on.
  4. Log query module, including local logs and device logs.
  5. System setting module, including system Settings (basic Settings, video parameters, database Settings, map configuration, serial port configuration, etc.), VCR management, camera management, polling configuration, user management, etc.

(2) Basic functions

  1. Support all kinds of video streams (RTSP, RTMP, HTTP, etc.), video files (MP4, RMVB, AVI, etc.), local USB camera playback.
  2. Supports multi-screen switching, including 1, 4, 6, 8, 9, 13, 16, 25, 36, 64 screen switching.
  3. Supports full-screen switching, including the right mouse button menu, toolbar buttons, and shortcut keys (Alt + Enter full-screen, esc exit full-screen).
  4. Supports video polling, including 1, 4, 9, and 16 screen polling. You can set polling groups (polling plan), polling interval, and bit stream types.
  5. Supports the ONVIF protocol, including device search, PTZ control, and device control (picture parameters, check time, system restart, and picture capture).
  6. Supports permission management. Different users have different permissions for modules, such as deleting logs and shutting down the system.
  7. Multiple databases are supported, including SQLite, mysql, SQLServer, PostgresQL, Oracle, and CNPC.
  8. You can set parameters such as resolution and frame rate for a local USB camera.
  9. All the docked modules automatically generate the corresponding menu to control the display and hide, right-click the title bar can be popup.
  10. You can display or hide all modules, reset the normal layout, or reset the full-screen layout.
  11. Double-click the device to display real-time preview videos, including picture maps, online maps, and offline maps.
  12. Drag the camera node to the corresponding form to play the video. You can also drag the local file to play the video directly.
  13. You can delete a video from the right mouse button, close the hover bar, or drag it outside the video monitoring panel.
  14. The device button on the picture map can be dragged freely to automatically save the location information. On the Baidu map, you can click to obtain the latitude and longitude information to update the device location.
  15. Any channel in the form of the video monitoring panel supports drag exchange and instant response.
  16. Encapsulating Baidu map, view switching, motion trajectory, device point, click the mouse to obtain latitude and longitude.
  17. Double click the node, drag the node, drag the form to change the position and other operations, are automatically updated and saved the last play address, the next software open automatic application.
  18. Lower right volume bar control, automatically hidden when losing focus, volume band mute icon.
  19. Support video capture, you can specify a single or all channel capture, the bottom toolbar also has a screenshot button.
  20. Supports automatic hiding of the mouse pointer and automatic full screen after timeout.
  21. Support onVIF PTZ control, can move PTZ camera up, down, left and right, including reset and focus adjustment.
  22. Support any ONVIF camera, including but not limited to Hikon, Dahua, Yusee, Tiandi weiye, Huawei, etc.
  23. Can save the video, can select the storage or single file storage, optional storage interval.
  24. The video stream communication mode can be set to TCP + UDP, and the video decoding mode can be set to speed first, quality first, balance, etc.
  25. You can set the software name in Chinese, English, and LOGO.
  26. The stored video files can be exported to a specified directory and uploaded to the server in batches.

(3) Features

  1. The main interface adopts the mode of docked form, and various components are added in the form of small modules, and any modules can be customized.
  2. The docking module can be dragged to any position to embed and hover, support maximum full screen, support multi-screen.
  3. Dual layout file storage mechanism, normal mode, full screen mode correspond to different layout scheme, automatic switching and saving, such as the full screen mode can highlight several modules transparent display in the specified position, more science fiction sense of modernization.
  4. The original ONVIF protocol mechanism, using the underlying protocol parsing (UDP broadcast search + HTTP request execution command) is more lightweight, easy to learn and expand, do not rely on any third-party components such as GSOAP.
  5. Original data import and export mechanism, cross-platform does not depend on any components, instantaneous data export.
  6. Built-in multiple original components, the universe value is super awesome, It includes data import and export components (export to XLS, PDF, print), database components (database management thread, automatic data cleaning thread, universal paging, data request, etc.), map components, video monitoring components, file multi-threaded transceiver components, ONVIF communication components, general browser kernel components, etc.
  7. Custom information box, error box, query box, and message box (in various formats) in the lower right corner.
  8. Exquisite skin peels, up to 17 sets of skin styles can be changed at will, all styles are unified, including menu, etc.
  9. Multiple buttons can be added to the video control hover bar, and buttons can also be added to the small toolbar at the bottom of the monitoring interface.
  10. Double click the camera node to automatically play the video, double click the node to automatically add the video, will automatically jump to the next, double click the parent node to automatically add all the videos under this node. Primary stream and substream are optional.
  11. VCR management and camera management: You can add, delete, modify, import or export printed information. The new device information is immediately applied to generate a tree list without restarting the system.
  12. Optional a variety of kernel free switching, FFMPEG, VLC, MPV, etc., can be set in pro. It is recommended to use FFMPEG, the most cross-platform, default to provide a good Linux and MAC platform compiled library.
  13. Support hard decoding, can set the hard decoding type (QSV, DXVA2, D3D11VA, etc.).
  14. Opengl rendering video by default, ultra low CPU resource occupation, support yuYV and NV12 two formats, very cow force.
  15. Highly customizable, users can easily derive their own functions on this basis, such as adding custom modules, operating modes, robot monitoring, uav monitoring, excavator monitoring, etc.
  16. Support XP, Win7, Win10, Linux, MAC, a variety of domestic systems (UOS, Kirin, Galaxy, etc.), embedded Linux and other systems.
  17. Complete comments, clear project structure, super detailed complete use of the development manual, down to the function of each code file, continuous iterations.

Three, experience address

  1. Experience address: pan.baidu.com/s/1d7TH_GEY… Extract code: 01JF File name: bin_video_system.zip.
  2. Domestic website: gitee.com/feiyangqing…
  3. International site: github.com/feiyangqing…
  4. Profile: blog.csdn.net/feiyangqing…
  5. Zhihu homepage: www.zhihu.com/people/feiy…
  6. Online documentation: feiyangqingyun. Gitee. IO/qwidgetdemo…

Four, the effect diagram

5. Core code

void frmVideoPlayImage::loadImage(a)
{
    int count = listFile.count(a);if (count == 0) {
        return;
    }

    // Play to the end
    if (listIndex >= count) {
        // Automatically switch to the next one
        if (ui->listWidget->currentRow() < ui->listWidget->count() - 1) {
            ui->listWidget->setCurrentRow(ui->listWidget->currentRow() + 1);
            on_listWidget_doubleClicked(a); }else {
            isPlay = false;
            timerPlay->stop(a); ui->sliderPosition->setValue(0);
            ui->labTimePlay->setText(QString("Sheet %1").arg(0));
            ui->labTimeAll->setText(QString("Total %1").arg(0));
            return; }}// Continuously send the image in the form of a signal, the corresponding associated slot function will automatically draw
    QImage image(listFile.at(listIndex));
    emit receiveImage(image);
    listIndex++;

    ui->labTimePlay->setText(QString("Sheet %1").arg(listIndex));
    ui->sliderPosition->setValue(listIndex);
}

void frmVideoPlayImage::addItem(const QString &text, const QString &data)
{
    QListWidgetItem *item = new QListWidgetItem;
    item->setText(text);
    item->setData(Qt::UserRole, QString(data));
    item->setCheckState(Qt::Unchecked);
    ui->listWidget->addItem(item);
}

void frmVideoPlayImage::on_btnSelect_clicked(a)
{
    QDate dateStart = ui->dateStart->date(a); QDate dateEnd = ui->dateEnd->date(a);if (dateStart > dateEnd) {
        QUIHelper::showMessageBoxError("The start time cannot be longer than the end time!".3);
        return;
    }

    // Convert the date to the date and time to calculate the number of days between the date and time. If the number of days exceeds the maximum, a message is displayed indicating that you do not need to continue
    if (dateStart.daysTo(dateEnd) >= 60) {
        QUIHelper::showMessageBoxError("You can only query for 60 days at a time!".3);
        return;
    }

    ui->listWidget->clear(a); QString filePath;if (ui->cboxType->currentText() = ="Store pictures") {
        filePath = AppData::ImageNormalPath;
    } else {
        filePath = AppData::ImageAlarmPath;
    }

    // Filter the specified channel
    QString channel = ui->cboxCh->currentText(a); QString ch =QString("Ch%1").arg(channel.mid(2, channel.length()));

    // Load the date directory with files into the list
    // And store the name of the image collection of the channel corresponding to the directory into the custom attribute
    // Then add the start time by one day until it is greater than the end time
    while (dateStart <= dateEnd) {
        // Generate a folder for the corresponding date
        QString dateName = dateStart.toString("yyyy-MM-dd");
        QString savePath = QString("% % 1/2").arg(filePath).arg(dateName);
        QDir saveDir(savePath);
        // Check whether the folder exists
        if (saveDir.exists()) {
            // Specify file extensions to be filtered in ascending order of time
            QStringList filter;
            filter << "*.jpg" << "*.png";
            QStringList fileNames = saveDir.entryList(filter, QDir::NoFilter, QDir::Time | QDir::Reversed);

            QStringList fullNames;
            foreach (QString fileName, fileNames) {
                QString name = savePath + "/" + fileName;
                if (channel == "All channels.") {
                    fullNames << name;
                } else {
                    if (fileName.startsWith(ch)) { fullNames << name; }}}int count = fullNames.count(a);if (count > 0) {
                QString text = QString("%1 %2 total: %3").arg(dateName).arg(channel).arg(count);
                addItem(text, fullNames.join("|"));
            }
        }

        dateStart = dateStart.addDays(1);
    }

    ui->labTip->setText(QString("Total %1 found").arg(ui->listWidget->count()));
}

void frmVideoPlayImage::on_btnDown_clicked(a)
{
    // Get all the selected file sets
    QStringList fileNames;
    int count = ui->listWidget->count(a);for (int row = 0; row < count; row++) {
        QListWidgetItem *item = ui->listWidget->item(row);
        if (item->checkState() == Qt::Checked) {
            fileNames << item->data(Qt::UserRole).toString().split("|"); }}if (fileNames.count() = =0) {
        QUIHelper::showMessageBoxError("No files selected!".3);
        return;
    }

    // The Save as dialog box is displayed
    QString path = QUIHelper::getFolderName(a);if(! path.isEmpty()) {
        // Copy files one by one
        foreach (QString fileName, fileNames) {
            QString name = fileName.split("/").last(a); QString targetFile = path +"/" + name;
            QFile::copy(fileName, targetFile);
        }

        DbQuery::addUserLog("Copy picture file");
        QUIHelper::showMessageBoxInfo("Copy the picture file!".3); }}QString frmVideoPlayImage::getHtml(a)
{
    // The following images are sample images stored in the resource file
    // The real application is changed to the absolute path of the image
    QStringList images;
#if 1
    images << ":/image/liuyifei0.jpg" << ":/image/liuyifei2.jpg" << ":/image/liuyifei1.jpg";
    images << ":/image/liuyifei0.jpg" << ":/image/liuyifei1.jpg" << ":/image/liuyifei2.jpg";
    images << ":/image/liuyifei2.jpg" << ":/image/liuyifei1.jpg" << ":/image/liuyifei0.jpg";
#else
    // The following indicates to fetch the image 1.jpg from the snap folder in the executable file directory
    images << QUIHelper::appPath() + "/snap/1.jpg";
#endif

    // There are many parameters that can be set. You can refer to the corresponding structure
    UavsReportData reportData;
    reportData.images = images;
    reportData.title = "Main title";
    reportData.subTitle = "Subtitle";
    reportData.name = "Drone -2021";

    // Get the size of the print area to calculate the width of the picture
    // The extra subtraction is for redundancy
    int imageWidth = (DataPrint::dataPrint->getPrinter() - >pageRect(QPrinter::DevicePixel).width() - 80) / 2;

    QStringList list;
    DataCreat::creatUavsReportHead(list, reportData);
    DataCreat::creatUavsReportBody(list, reportData, imageWidth);
    return list.join("");
}

void frmVideoPlayImage::dataout(int type)
{
    DataContent dataContent;
    dataContent.fileName = QUIHelper::appPath() + "/db/html.pdf";
    dataContent.pageMargin = QMargins(15.20.15.20);

    // Initialize once to ensure successful object instantiation
    DataHelper::Init(a); DataPrint::dataPrint->init(a); DataPrint::dataPrint->setDataContent(dataContent);

    // You need to set the setDataContent to reset the HTML to use the print area
    // the first time you setDataContent, you can get the exact print area
    dataContent.html = getHtml(a); DataPrint::dataPrint->setDataContent(dataContent);

    if (type == 0) {
        DataPrint::dataPrint->print(a); }else if (type == 1) {
        DataPrint::dataPrint->open(a); DataPrint::dataPrint->close(a); QUIHelper::openFile(dataContent.fileName, "Exporting an Alarm Report"); }}void frmVideoPlayImage::on_btnPdf_clicked(a)
{
    dataout(1);
}

void frmVideoPlayImage::on_btnPrint_clicked(a)
{
    dataout(0);
}

void frmVideoPlayImage::on_btnPlayVideo_clicked(a)
{
    if (ui->listWidget->currentRow()"0) {
        return; } isPlay = ! isPlay;if (isPlay) {
        IconHelper::setIcon(ui->btnPlayVideo, 0xf28d, btnRadius);
        timerPlay->start(a); }else {
        IconHelper::setIcon(ui->btnPlayVideo, 0xf144, btnRadius);
        timerPlay->stop();
    }
}

void frmVideoPlayImage::on_listWidget_doubleClicked(a)
{
    // Retrieve all queried image names corresponding to this folder from properties
    listFile = ui->listWidget->currentItem() - >data(Qt::UserRole).toString().split("|");
    int count = listFile.count(a);if (count > 0) {
        listIndex = 0;
        ui->labTimeAll->setText(QString("Total %1").arg(count));
        ui->sliderPosition->setRange(0, count - 1);
        ui->sliderPosition->setValue(0);

        isPlay = true;
        IconHelper::setIcon(ui->btnPlayVideo, 0xf28d, btnRadius);
        timerPlay->start();
    }
}

void frmVideoPlayImage::on_sliderFps_clicked(a)
{
    timerPlay->setInterval(ui->sliderPosition->value(*)100);
}

void frmVideoPlayImage::on_sliderFps_sliderMoved(int value)
{
    timerPlay->setInterval(value * 100);
}

void frmVideoPlayImage::on_sliderPosition_clicked(a)
{
    listIndex = ui->sliderPosition->value(a); }void frmVideoPlayImage::on_sliderPosition_sliderMoved(int value)
{
    listIndex = value;
}

void frmVideoPlayImage::on_listWidget_itemPressed(QListWidgetItem *item)
{
    bool checked = (item->checkState() == Qt::Checked);
    item->setCheckState(checked ? Qt::Unchecked : Qt::Checked);
}
Copy the code