多路h265监控录放开发-(15)回放页面中的三个槽函数进行视频的录放(0.1版本项目完结篇)

news2024/10/5 13:45:39

xviewer.h 中的回放页面的三个槽函数:

    void SelectCamera(QModelIndex index);//选择摄像机129
    void SelectDate(QDate date);        //选择日期129
    void PlayVideo(QModelIndex index);  //选择时间播放视频129

SelectCamera槽函数解析:

点击相机列表日历显示该相机所录制的视频,如有则日期标为红色并放大字体,如四号有录制的视频那么四号变红放大点击进去可以回放视频


void XViewer::SelectCamera(QModelIndex index)//选择摄像机
{
    qDebug() << "SelectCamera" << index.row();
    auto conf = XCameraConfig::Instance();
    XCameraData cam = conf->GetCam(index.row()); //获取相机参数
    if (cam.name[0] == '\0')
    {
        return;
    }
    //相机视频存储路径
    stringstream ss;
    ss << cam.save_path << "/" << index.row() << "/";

    //遍历此目录
    QDir dir(C(ss.str().c_str()));
    if (!dir.exists())
        return;
    //获取目录下文件列表
    QStringList filters;
    filters << "*.mp4" << "*.avi";
    dir.setNameFilters(filters);//筛选


    ui.cal->ClearDate();
    //所有文件列表
    auto files = dir.entryInfoList();
    for (auto file : files)
    {
        //"cam_2020_09_04_17_54_58.mp4"
        QString filename = file.fileName();

        //去掉cam_ 和 .mp4
        auto tmp = filename.left(filename.size() - 4);
        tmp = tmp.right(tmp.length() - 4);
        //2020_09_04_17_54_58
        auto dt = QDateTime::fromString(tmp, "yyyy_MM_dd_hh_mm_ss");
        qDebug() << dt.date();//yyyy_MM_dd形式
        ui.cal->AddDate(dt.date());
        //qDebug() << file.fileName();
130
        XCamVideo video;
        video.datetime = dt;
        video.filepath = file.absoluteFilePath();
        cam_videos[dt.date()].push_back(video);

130
    }

    //重新显示日期
    ui.cal->showNextMonth();
    ui.cal->showPreviousMonth();


}

上面用到的结构体 和数据结构map
struct XCamVideo
{
    QString filepath;
    QDateTime datetime;
};
static map<QDate, vector<XCamVideo> > cam_videos;//通过点击QDate来索引一组数据
  • qDebug() << "SelectCamera" << index.row();

    • 打印调试信息,显示被选中的摄像机的行号。
  • auto conf = XCameraConfig::Instance();

    • 获取 XCameraConfig 类的单例实例,假设这是一个配置管理器。
  • XCameraData cam = conf->GetCam(index.row());

    • 通过行号获取对应的摄像机数据 XCameraData
  • if (cam.name[0] == '\0') { return; }

    • 检查摄像机的名称是否为空,如果是,则返回,终止函数执行。
  • stringstream ss; ss << cam.save_path << "/" << index.row() << "/";
  •  创建一个 stringstream 对象,将摄像机视频存储路径和行号组合成一个完整的目录路径。
  • QDir dir(C(ss.str().c_str()));
    • 创建一个 QDir 对象,表示生成的目录路径。
  • if (!dir.exists()) return;
    • 检查目录是否存在,如果不存在,返回,终止函数执行
    • QStringList filters; filters << "*.mp4" << "*.avi";
      • 创建一个 QStringList 对象,并添加视频文件的扩展名过滤器。
    • dir.setNameFilters(filters);
      • 设置 QDir 对象的名称过滤器,使其只包含指定扩展名的文件。
  • ui.cal->ClearDate();
    • 清除日历控件中的所有日期。
  • auto files = dir.entryInfoList();
    • 获取目录中的所有文件列表
  • for (auto file : files)
    • 遍历所有文件。
  • QString filename = file.fileName();
    • 获取文件名。
  • auto tmp = filename.left(filename.size() - 4);
    • 去掉文件名的扩展名部分(后四个字符)。
  • tmp = tmp.right(tmp.length() - 4);
    • 去掉文件名前缀部分(前四个字符)。
  • auto dt = QDateTime::fromString(tmp, "yyyy_MM_dd_hh_mm_ss");
    • 将处理后的文件名转换为 QDateTime 对象。
  • qDebug() << dt.date();
    • 打印调试信息,显示日期部分。
  • ui.cal->AddDate(dt.date());
    • 将日期添加到日历控件中。
  • XCamVideo video;
    • 创建一个 XCamVideo 对象。
  • video.datetime = dt; video.filepath = file.absoluteFilePath();
    • 设置 XCamVideo 对象的日期时间和文件路径。
  • cam_videos[dt.date()].push_back(video);
    • 将视频对象添加到以日期为键的 cam_videos 映射中,该映射在vector中
  • ui.cal->showNextMonth(); ui.cal->showPreviousMonth();
  • 重新显示日历控件的日期,确保更新后的日期显示正确。

总结

这个槽函数 SelectCamera(QModelIndex index) 主要完成以下任务:

  1. 获取选中的摄像机的配置信息。
  2. 构造摄像机视频文件存储路径。
  3. 检查路径是否存在,并筛选出 .mp4.avi 文件。
  4. 清除日历控件中的日期。
  5. 遍历筛选出的文件列表,提取文件名中的日期信息并转换为 QDateTime 对象。
  6. 将日期添加到日历控件中,并将视频文件的信息保存到 cam_videos 映射中。
  7. 重新显示日历控件,以反映更新后的日期。

SelectDate槽函数解析: 

点击日历上的日期显示该日期录制的视频时间列表

void XViewer::SelectDate(QDate date)        //选择日期
{
    qDebug() << "SelectDate" << date.toString();

    vector<XCamVideo> dates = cam_videos[date];
    //dates存的以下数据
    //struct XCamVideo
    //{
    //    QString filepath;
    //    QDateTime datetime;
    //};
    ui.time_list->clear();
    for (auto d : dates)
    {
        //再QListwidget控件上添加项,显示的是具体时间QTime类型
        QListWidgetItem* item = new QListWidgetItem(d.datetime.time().toString());

        //将视频文件的路径存储在列表项的用户角色数据中。
        //Qt::UserRole 是一个预定义的角色值,用于存储用户自定义的数据。
        //在点击QListwidget列表项的时候会用到(PlayVideo槽函数中)
        item->setData(Qt::UserRole, d.filepath);
        ui.time_list->addItem(item);
    }
}

  • auto dates = cam_videos[date];
    • cam_videos 映射中获取与选定日期相关的视频数据列表。cam_videos 是一个以日期为键、以视频数据列表为值的映射。
  • ui.time_list->clear();
    • 清空 time_list 列表控件,以便重新填充新的数据。
  • for (auto d : dates)

    • 遍历选定日期的视频数据列表。
  • auto item = new QListWidgetItem(d.datetime.time().toString());

    • 创建一个新的 QListWidgetItem 项目,将视频文件的时间信息(提取自 d.datetime)设置为列表项的显示文本。
  • item->setData(Qt::UserRole, d.filepath);
    • 将视频文件的路径存储在列表项的用户角色数据中。Qt::UserRole 是一个预定义的角色值,用于存储用户自定义的数据。
  • ui.time_list->addItem(item);
    • 将创建的列表项添加到 time_list 列表控件中。

总结

槽函数 SelectDate(QDate date) 的主要功能是:

  1. 获取选定日期的视频文件数据列表。
  2. 清空当前时间列表控件。
  3. 遍历视频数据列表,为每个视频创建一个列表项,显示视频的时间信息。
  4. 将视频文件路径作为自定义数据存储在列表项中。
  5. 将列表项添加到时间列表控件中。

PlayVideo槽函数解析: 

void XViewer::PlayVideo(QModelIndex index)  //选择时间播放视频
{
    qDebug() << "PlayVideo" << index.row();
    auto item = ui.time_list->currentItem();
    if (!item)return;
    QString path = item->data(Qt::UserRole).toString();
    qDebug() << path;

    //建一个新的QT类 XPlayVideo,一个新的UI视图用于渲染录像播放录像
    static XPlayVideo play;
    play.Open(path.toLocal8Bit());
    play.show();
}

 其中XPlayVideo类的Open函数在 XplayVideo.cpp文件中:

XplayVideo.cpp

#include "xplayvideo.h"
void XPlayVideo::timerEvent(QTimerEvent* ev)
{
    if (!view_)return;
    auto f = decode_.GetFrame();
    if (!f)return;
    view_->DrawFrame(f);
    XFreeFrame(&f);
}
void XPlayVideo::Close()
{
    //关闭上次数据
    demux_.Stop();
    decode_.Stop();
    if (view_)
    {
        view_->Close();
        delete view_;
        view_ = nullptr;
    }
}

void XPlayVideo::closeEvent(QCloseEvent* ev)
{
    Close();
}


bool XPlayVideo::Open(const char* url)
{

    if (!demux_.Open(url)) //解封装
    {
        return false;
    }
    auto vp = demux_.CopyVideoPara();
    if (!vp)
        return false;
    if (!decode_.Open(vp->para))//解码
    {
        return false;
    }
    demux_.set_next(&decode_);

    if (!view_)
        view_ = XVideoView::Create();
    view_->set_win_id((void*)winId());
    if (!view_->Init(vp->para)) //SDL渲染
        return false;
    //demux_.set_syn_type(XSYN_VIDEO);
    demux_.Start();
    decode_.Start();
    return true;
}
XPlayVideo::XPlayVideo(QWidget* parent)
    : QWidget(parent)
{
    ui.setupUi(this);
    startTimer(10);
}

XPlayVideo::~XPlayVideo()
{
    Close();
}

XPlayVideo::Open函数解析 

if (!demux_.Open(url))

  • 尝试打开并解封装(demux)视频流。如果失败,返回 false 终止操作。

auto vp = demux_.CopyVideoPara();

  • 复制视频参数。如果获取视频参数失败,返回 false 终止操作。
  • vp 是一个包含视频流参数的对象。

if (!decode_.Open(vp->para))

  • 尝试使用视频参数打开解码器。如果失败,返回 false 终止操作。

demux_.set_next(&decode_);

  • 设置解封装器的下一个处理模块为解码器。可能意味着解封装器在解封装出数据后会将其传递给解码器进行解码。

if (!view_) view_ = XVideoView::Create();

  • 如果渲染视图对象 view_ 尚未创建,则创建一个新的渲染视图对象。

view_->set_win_id((void*)winId());

  • 设置渲染视图对象的窗口 ID,以便渲染图像。winId() 可能是一个返回窗口 ID 的方法。

if (!view_->Init(vp->para))

  • 初始化渲染视图对象。如果初始化失败,返回 false 终止操作。
  • vp->para 传递视频参数用于初始化。
  • demux_.Start();decode_.Start();

    • 启动解封装器和解码器。
    • demux_.Start() 开始解封装过程。
    • decode_.Start() 开始解码过程。
  • return true;

    • 如果所有步骤成功,返回 true 表示打开视频流成功。

执行上面代码的Open函数中的demux_.start启动主线程后有一个控制播放速度的功能模块,如果不进行控制播放速度,播放录像的视频速度非常快,以下是解封装的主线程函数代码:

demux_task.cpp 

void XDemuxTask::Main()
{
    AVPacket pkt;
    while (!is_exit_)
    {
        if (!demux_.Read(&pkt))
        {
            //读取失败
            cout << "-" << flush;
            if (!demux_.is_connected())//如果断开连接,需要重新连接
            {
                Open(url_, timeout_ms_);
            }

            this_thread::sleep_for(1ms);
            continue;
        }
        cout << "." << flush;

        //播放速度控制132 132 132 132 132 132
        if (syn_type_ == XSYN_VIDEO &&
            pkt.stream_index == demux_.video_index())
        {
            auto dur = demux_.RescaleToMs(pkt.duration, pkt.stream_index);
            if (dur <= 0)
                dur = 40;
            //pkt.duration
            MSleep(dur);
        }

        Next(&pkt);
        av_packet_unref(&pkt);

        this_thread::sleep_for(1ms);
    }
}

其中调用了RescaleToMs:把duration转成毫秒的函数位于xformat.cpp中 

xformat.cpp

//把pts dts duration 值转为毫秒  132  132
long long XFormat::RescaleToMs(long long pts, int index)
{
    unique_lock<mutex> lock(mux_);
    if (!c_ || index <0 || index > c_->nb_streams)return 0;
    auto in_timebase = c_->streams[index]->time_base;

    AVRational out_timebase = { 1,1000 };//输出timebase 毫秒
    return av_rescale_q(pts, in_timebase, out_timebase);
}

该函数将转换时间基数调整播放速度

运行结果: 

没设置XplayVideo窗口的可拖动功能所以这个窗口固定的 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1858737.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【路由交换技术】Cisco Packet Tracer基础入门教程(四)

Hello各位&#xff0c;好久不见&#xff0c;第四期我准备讲一下Packet Tracer中DHCP的配置&#xff0c;使用方法。 本章实验我们将拓扑中的某个路由器作为DHCP服务器&#xff08;它仍然可作为路由器使用&#xff09;&#xff0c;通过命令配置DHCP服务。独立的服务器可通过图形化…

【AI大模型】GPTS 与 Assistants API

前言 2023 年 11 月 6 日&#xff0c;OpenAI DevDay 发表了一系列新能力&#xff0c;其中包括&#xff1a;GPT Store 和 Assistants API。 GPTs 和 Assistants API 本质是降低开发门槛 可操控性和易用性之间的权衡与折中&#xff1a; 更多技术路线选择&#xff1a;原生 API、…

创新指南|品牌电商新策略:五大转型思路与RGM举措

在流量红利过去的背景下&#xff0c;品牌电商面对多渠道运营的难题&#xff0c;如缺乏统盘经营、绩效管理分散、价格战失控、用户体验不足以及流量过度依赖&#xff0c;品牌电商如何有效应对这些挑战&#xff0c;本文从5个维度探讨全渠道电商RGM破局之路&#xff0c;实现品牌的…

利用chrome_remote_interface实现程序化、自动化Web安全测试

2024软件测试面试刷题&#xff0c;这个小程序&#xff08;永久刷题&#xff09;&#xff0c;靠它快速找到工作了&#xff01;&#xff08;刷题APP的天花板&#xff09;-CSDN博客跳槽涨薪的朋友们有福了&#xff0c;今天给大家推荐一个软件测试面试的刷题小程序。https://blog.c…

MOE学习笔记

MOE网络结构 和传统的 transformer 网络结构相比&#xff0c;我们将 Transformer 模型的每个 FFN 层替换为 MoE 层&#xff0c;MoE 层由门网络&#xff08;Router&#xff09;和一定数量的专家&#xff08;Expert&#xff09;组成。 这些 Expert 其实也是 FFN 层&#xff0c;…

LeetCode 算法:二叉树的直径 c++

原题链接&#x1f517;&#xff1a;二叉树的直径 难度&#xff1a;简单⭐️ 题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由…

审美进阶:7个小程序模板,助你提高设计感!

小程序是一种无需下载和安装即可使用的应用程序。小程序实现了应用程序“触手可及”的梦想。用户可以通过扫描或搜索打开应用程序。对于开发者来说&#xff0c;小程序也大大降低了开发成本。因此&#xff0c;越来越多的品牌争相制作小程序应用程序。本文将为您带来优秀的微信小…

ESP32-S3方案应用设备无线交互技术,产品远程控制与语音交互

在物联网和人工智能(AI)技术融合的浪潮中&#xff0c;ESP32-S3芯片以其卓越的性能和多功能性&#xff0c;成为智能家居和工业自动化领域的明星产品。 ESP32-S3是一款基于Xtensa LX6处理器的嵌入式系统级芯片&#xff0c;具有高效、低功耗的特点。集成的Wi-Fi和蓝牙功能&#x…

结合人工智能的在线教育系统:开发与实践

人工智能&#xff08;AI&#xff09;正在革新各行各业&#xff0c;教育领域也不例外。结合AI技术的在线教育系统能够提供个性化的学习体验、智能化的教学辅助和高效的数据分析&#xff0c;从而大大提升教育质量和学习效果。本文将探讨结合AI技术的在线教育系统的开发与实践&…

【数据结构】比较顺序表和链表的区别(优缺点),细讲CPU高速缓存命中率

目录 一、顺序表和链表的区别【表格】 二、顺序表优缺点 三、链表优缺点 四、缓存命中率&#xff08;缓存利用率&#xff09; ❥ 主存和本地二级存储 ❥ 寄存器和三级缓存 ❥ 顺序表缓存命中率 ❥ 链表缓存命中率 一、顺序表和链表的区别【表格】 不同点顺序表链表&am…

【十二】图解 Spring 核心数据结构:BeanDefinition

图解 Spring 核心数据结构&#xff1a;BeanDefinition 简介 使用spring框架的技术人员都知道spring两个大核心技术IOC和AOP&#xff0c;随着投入更多的时间去学习spring生态&#xff0c;越发觉得spring的发展不可思议&#xff0c;一直都是引领着Java EE的技术变革&#xff0c;这…

MySQL之可扩展性(一)

可扩展性 概述 有些应用仅仅适用于一台或少数几台服务器&#xff0c;那么哪些可扩展性建议是和这些应用相关的呢&#xff1f;大多数人从不会维护超大规模的系统&#xff0c;并且通常也无法效仿在主流大公司所使用的策略。选择一个合适的策略能够大大地节约时间和金钱。 MySQL…

ONLYOFFICE 桌面编辑器8.1---一个高效且强大的办公软件

软件介绍 ONLYOFFICE 桌面编辑器经过不断的更新换代现在迎来了&#xff0c;功能更加强大的ONLYOFFICE 桌面编辑器8.1是一个功能强大的办公套件&#xff0c;专为多平台设计&#xff0c;包括Windows、Linux和macOS。它提供了一套全面的办公工具&#xff0c;包括文档处理、电子表…

IO-Iink事件

IO-LINK事件功能 IO-Link的事件功能是其通信协议中的一项重要特性&#xff0c;主要用于传输设备的故障信息和维护信息。IO-Link支持三种数据类型&#xff1a;过程数据、参数数据和事件数据。其中&#xff0c;事件数据就是用于此目的。 当IO-Link设备&#xff08;如传感器或执…

大咖共话|智领未来,数字化革新生命健康产业

在数字化浪潮席卷全球的今天&#xff0c;生命健康产业正迎来前所未有的发展机遇。6月20日&#xff0c;“第三届滨海中关村协同创新发展交流会”在北京隆重举办&#xff0c;唯迈医疗与业界大咖齐聚一堂&#xff0c;在“数字化赋能驱动生命健康产业发展”圆桌论坛上&#xff0c;共…

记一下 Stream 流操作

Java Stream流 创建流 Collection.stream() / Collection.parallelStream()&#xff1a;从集合生成流&#xff0c;后者为并行流。 List<String> list new ArrayList<>(); Stream<String> stream list.stream(); //获取一个顺序流 Stream<String> …

《C++ Primer》导学系列:第 8 章 - IO库

8.1 IO类 C标准库提供了一套丰富的输入输出&#xff08;IO&#xff09;类&#xff0c;用于处理数据的输入输出操作。这些类位于<iostream>头文件中&#xff0c;包括处理标准输入输出的istream和ostream类&#xff0c;处理文件输入输出的ifstream和ofstream类&#xff0c…

Java 8 Date and Time API

Java 8引入了新的日期和时间API&#xff0c;位于java.time包下&#xff0c;旨在替代旧的java.util.Date和java.util.Calendar类。新API更为简洁&#xff0c;易于使用&#xff0c;并且与Joda-Time库的一些理念相吻合。以下是Java 8 Date and Time API中几个核心类的简要概述&…

构建开源多模态RAG系统

在这个新的冒险中&#xff0c;我们将深入研究使用开源大型语言多模态&#xff08;LLMM&#xff09;构建检索增强型生成&#xff08;RAG&#xff09;系统的过程。值得注意的是&#xff0c;我们的重点是在不依赖LangChain或Llama索引的情况下实现这一点&#xff1b;相反&#xff…

Trilium windows上修改笔记目录,创建多个笔记空间方法

一开始使用trilium会非常的不舒服&#xff0c;不像是obsidian可以创建多个笔记空间&#xff0c;指定多个笔记目录。这里摸索到了解决方案 修改目录的方法一 ——修改系统环境变量 打开控制面板-系统-高级系统设置 新增如上条目 修改目录的方法二——直接写bat脚本运行 新建位…