Qt展厅播放器/多媒体播放器/中控播放器/帧同步播放器/硬解播放器/监控播放器

news2025/2/6 4:42:26

一、前言说明

音视频开发除了应用在安防监控、视频网站、各种流媒体app开发之外,还有一个小众的市场,那就是多媒体展厅场景,这个场景目前处于垄断地位的软件是HirenderS3,做的非常早而且非常全面,都是通用的需求,这个场景需求,在播放这块,有几个明显的需求是,播放的视频文件分辨率特别大,一般是4K/8K甚至到16K,展厅的屏幕很大,分辨率小了的话,不够清晰,所以至少会上4K分辨率,一般超过2K的分辨率,一定要上硬解码,不然CPU很可能撑不住,这里问题就来了,在底层(不清楚是操作系统层面还是硬件层面),264只能支持4K硬解,265支持到8K硬解,再往上的分辨率都不支持的,而现在不少的多媒体文件是8K的264格式,12K/16K的265,那怎么办,不开启硬解的话,资源极其紧张,几乎是快占满的节奏,所以要从逻辑层面去优化,一个办法是外接多个显卡对应显示器,文件切割成多个4K或者8K,每个文件指定一个显卡去硬解,显示在指定的显示器上。一个办法是用多个显示窗体拼接,每个播放窗体都可以硬解。

还有个重要需求点是同步问题,如果是本地多个画面拼接,则需要帧同步,不然开起来多画面是不够衔接的,大于1帧的误差能够肉眼可见,多个电脑之间不同播放器也需要帧同步,所以就产生了两种帧同步需求,一个是本地帧同步,一个是网络帧同步,本地帧同步主要是控制同时解码同一帧后同时刷新显示,网络帧同步比较麻烦,因为必须通过网络数据通知当前都是播放第一帧,这个网络通信都是有延迟的,所以还需要考虑延迟的时间。

二、效果图

在这里插入图片描述
在这里插入图片描述

三、相关代码

#include "synclocal.h"
#include "qthelper.h"
#include "frmplay.h"

SINGLETON_IMPL(SyncLocal)
QDateTime SyncLocal::SyncTime = QDateTime::currentDateTime().addDays(-1);
SyncLocal::SyncLocal(QObject *parent) : QThread(parent)
{
    isStop = false;
    this->reset();

    syncInterval = 5;
    syncOffset = 15;
    syncSleep = 500;
    updateInterval = 10;
}

SyncLocal::~SyncLocal()
{
    this->stop();
}

void SyncLocal::run()
{
    while (!isStop) {
        this->checkPosition();
        this->checkSync();
        this->checkPause();
        this->updateWidget();

        count++;
        msleep(updateInterval);
        //qDebug() << TIMEMS << "111" << updateInterval << count;
    }

    isStop = false;
    this->reset();
}

void SyncLocal::checkPosition()
{
    //同步间隔0表示不启用/至少要2个窗体才需要同步
    int size = frmPlay::widgets.size();
    if (size < 2 || isSync || isPasue) {
        count = 0;
        return;
    }

    //永远同步到到第一个窗体/处于非播放状态/或者暂停状态不用继续
    frmPlay *widget = frmPlay::widgets.first();
    if (!widget->isPlaying() || widget->isPaused()) {
        return;
    }

    //优先执行手动同步指令/-1则同步到第一个窗体/>=0则同步到对应位置
    if (syncPosition >= -1) {
        position = (syncPosition == -1 ? widget->position() : syncPosition);
        count = 0;
        isSync = true;
        qDebug() << TIMEMS << "hand" << position;
        return;
    }

    //同步间隔0表示不启用
    if (syncInterval == 0) {
        count = 0;
        return;
    }

    //计算同步间隔需要循环多少次
    int maxCount = syncInterval * 1000 / updateInterval;
    //到了需要同步的时候执行同步
    if (count < maxCount) {
        return;
    }

    count = 0;    

    //刚开始或者快结束先不同步
    position = widget->position();
    if (position < 2000 || qAbs(widget->duration() - position) < 2000) {
        return;
    }

    for (int i = 1; i < size; ++i) {
        offset = position - frmPlay::widgets.at(i)->position();
        qDebug() << TIMEMS << "posi" << position << "\t" << offset;
        if (qAbs(offset) >= syncOffset) {
            isSync = true;
            break;
        }
    }
}

void SyncLocal::checkSync()
{
    //同步标志位为真则先同步
    if (isSync) {
        count = 0;
        isSync = false;
        isPasue = true;
        SyncTime = QDateTime::currentDateTime();
        qDebug() << TIMEMS << "seek" << position;

        //先暂停再执行设置进度
        foreach (frmPlay *widget, frmPlay::widgets) {
            widget->pause();
            widget->seek(position);
        }
    }
}

void SyncLocal::checkPause()
{
    //暂停阶段说明刚才执行过同步/等待一段时间重新播放
    if (isPasue) {
        qint64 time = SyncTime.msecsTo(QDateTime::currentDateTime());
        if (time >= syncSleep) {
            foreach (frmPlay *widget, frmPlay::widgets) {
                widget->next();
            }

            count = 0;
            isPasue = false;
            syncPosition = -2;
            emit receiveSync(offset);
            qDebug() << TIMEMS << "play" << position;
        }
    }
}

void SyncLocal::updateWidget()
{
    //刷新界面用来触发绘制
    foreach (frmPlay *widget, frmPlay::widgets) {
        widget->updateVideo();
    }
}

void SyncLocal::setSyncInterval(int syncInterval)
{
    this->reset();
    this->syncInterval = syncInterval;
}

void SyncLocal::setSyncOffset(int syncOffset)
{
    this->syncOffset = syncOffset;
}

void SyncLocal::setSyncSleep(int syncSleep)
{
    this->syncSleep = syncSleep;
}

void SyncLocal::setUpdateInterval(int updateInterval)
{
    this->updateInterval = updateInterval;
}

void SyncLocal::stop()
{
    if (this->isRunning()) {
        this->isStop = true;
        this->wait();
    }
}

void SyncLocal::reset()
{
    this->count = 0;
    this->isSync = false;
    this->isPasue = false;
    this->syncPosition = -2;
}

//-1则同步到第一个窗体/>=0则同步到对应位置
void SyncLocal::sync(qint64 position)
{
    //至少要两个窗体才能同步/处于暂停阶段说明上一个同步还没执行完成
    if (frmPlay::widgets.size() >= 2 && !isPasue && syncPosition == -2) {
        this->syncPosition = position;
    }
}

四、相关地址

  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_sync。

五、功能特点

  1. 实时帧同步,本地无缝拼接多个视频。
  2. 支持网络同步,可选主控端和被控端,主控端将本地播放的进度实时同步到被控端。
  3. 网络同步支持组播、广播、单播三种模式,默认组播,既可以跨网段,也可以避免广播数据风暴。
  4. 默认开启自动同步,也可以手动同步和复位同步,手动同步是立即执行一次同步,将第一个视频的进度同步到其他视频文件,复位同步是将所有视频播放进度切换到最开始0的位置。
  5. 支持各种视音频文件,包括但不限于mp4/mov/mkv/rmvb/avi等格式。
  6. 硬解码和GPU绘制,最大化利用硬件资源,支持qsv/cuda/dxva2/d3d11va/vaapi等硬解码。
  7. 极低的CPU占用,8K30fps只占不到1%的CPU,解码和绘制全部交给GPU。
  8. 提供示例按照行列生成多个视频播放窗口,每个窗口可以选择不同的视频文件,在手动同步模式下,可以切换任意一个视频播放进度,会将所有的视频按照这个进度同步。
  9. 自动循环播放视频文件,无缝切换循环播放,看起来非常丝滑。
  10. 支持Qt4/Qt5/Qt6所有版本,支持各种操作系统包括国产OS和嵌入式OS。

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

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

相关文章

html中的表格属性以及合并操作

表格用table定义&#xff0c;标签标题用caption标签定义&#xff1b;用tr定义表格的若干行&#xff1b;用td定义若干个单元格&#xff1b;&#xff08;当单元格是表头时&#xff0c;用th标签定义&#xff09;&#xff08;th标签会略粗于td标签&#xff09; table的整体外观取决…

html的字符实体和颜色表示

在HTML中&#xff0c;颜色可以通过以下几种方式表示&#xff0c;以下是具体的示例&#xff1a; 1. 十六进制颜色代码 十六进制颜色代码以#开头&#xff0c;后面跟随6个字符&#xff0c;每两个字符分别表示红色、绿色和蓝色的强度。例如&#xff1a; • #FF0000&#xff1a;纯红…

unordered_map/set的哈希封装

【C笔记】unordered_map/set的哈希封装 &#x1f525;个人主页&#xff1a;大白的编程日记 &#x1f525;专栏&#xff1a;C笔记 文章目录 【C笔记】unordered_map/set的哈希封装前言一. 源码及框架分析二.迭代器三.operator[]四.使用哈希表封装unordered_map/set后言 前言 哈…

idea中git的简单使用

提交&#xff0c;推送直接合并 合到哪个分支就到先切到哪个分支

Fastdds学习分享_xtpes_发布订阅模式及rpc模式

在之前的博客中我们介绍了dds的大致功能&#xff0c;与组成结构。本篇博文主要介绍的是xtypes.分为理论和实际运用两部分.理论主要用于梳理hzy大佬的知识&#xff0c;对于某些一带而过的部分作出更为详细的阐释&#xff0c;并在之后通过实际案例便于理解。案例分为普通发布订阅…

开发板上Qt运行的环境变量的三条设置语句的详解

在终端中运行下面三句命令用于配置开发板上Qt运行的环境变量&#xff1a; export QT_QPA_GENERIC_PLUGINStslib:/dev/input/event1 export QT_QPA_PLATFORMlinuxfb:fb/dev/fb0 export QT_QPA_FONTDIR/usr/lib/fonts/设置成功后可以用下面的语句检查设置成功没有 echo $QT_QPA…

语言月赛 202412【顽强拼搏奖的四种发法】题解(AC)

》》》点我查看「视频」详解》》》 [语言月赛 202412] 顽强拼搏奖的四种发法 题目描述 在 XCPC 竞赛里&#xff0c;会有若干道题目&#xff0c;一支队伍可以对每道题目提交若干次。我们称一支队伍对一道题目的一次提交是有效的&#xff0c;当且仅当&#xff1a; 在本次提交…

自定义数据集 使用scikit-learn中svm的包实现svm分类

引入必要的库 import numpy as np from sklearn.datasets import make_classification from sklearn.model_selection import train_test_split from sklearn.svm import SVC from sklearn.metrics import accuracy_score, classification_report 生成自定义数据集 X, y ma…

有用的sql链接

『SQL』常考面试题&#xff08;2——窗口函数&#xff09;_sql的窗口函数面试题-CSDN博客 史上最强sql计算用户次日留存率详解&#xff08;通用版&#xff09;及相关常用函数 -2020.06.10 - 知乎 (zhihu.com) 1280. 学生们参加各科测试的次数 - 力扣&#xff08;LeetCode&…

【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.27 NumPy+Pandas:高性能数据处理的黄金组合

2.27 NumPyPandas&#xff1a;高性能数据处理的黄金组合 目录 #mermaid-svg-x3ndEE4hrhO6WR6H {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-x3ndEE4hrhO6WR6H .error-icon{fill:#552222;}#mermaid-svg-x3ndEE4hr…

第一个3D程序!

运行效果 CPP #include <iostream> #include <fstream> #include <string> #include <cmath>#include <GL/glew.h> #include <GLFW/glfw3.h> #include <glm/glm.hpp> #include <glm/gtc/type_ptr.hpp> #include <glm/gtc/…

NeuralCF 模型:神经网络协同过滤模型

实验和完整代码 完整代码实现和jupyter运行&#xff1a;https://github.com/Myolive-Lin/RecSys--deep-learning-recommendation-system/tree/main 引言 NeuralCF 模型由新加坡国立大学研究人员于 2017 年提出&#xff0c;其核心思想在于将传统协同过滤方法与深度学习技术相结…

第二十三章 MySQL锁之表锁

目录 一、概述 二、语法 三、特点 一、概述 表级锁&#xff0c;每次操作锁住整张表。锁定粒度大&#xff0c;发生锁冲突的概率最高&#xff0c;并发度最低。应用在MyISAM、InnoDB、BDB等存储引擎中。 对于表级锁&#xff0c;主要分为以下三类&#xff1a; 1. 表锁 2. 元数…

【Uniapp-Vue3】获取用户状态栏高度和胶囊按钮高度

在项目目录下创建一个utils文件&#xff0c;并在里面创建一个system.js文件。 在system.js中配置如下代码&#xff1a; const SYSTEM_INFO uni.getSystemInfoAsync();// 返回状态栏高度 export const getStatusBarHeight ()> SYSTEM_INFO.statusBarHeight || 15;// 返回胶…

通向AGI之路:人工通用智能的技术演进与人类未来

文章目录 引言:当机器开始思考一、AGI的本质定义与技术演进1.1 从专用到通用:智能形态的范式转移1.2 AGI发展路线图二、突破AGI的五大技术路径2.1 神经符号整合(Neuro-Symbolic AI)2.2 世界模型架构(World Models)2.3 具身认知理论(Embodied Cognition)三、AGI安全:价…

将ollama迁移到其他盘(eg:F盘)

文章目录 1.迁移ollama的安装目录2.修改环境变量3.验证 背景&#xff1a;在windows操作系统中进行操作 相关阅读 &#xff1a;本地部署deepseek模型步骤 1.迁移ollama的安装目录 因为ollama默认安装在C盘&#xff0c;所以只能安装好之后再进行手动迁移位置。 # 1.迁移Ollama可…

Java自定义IO密集型和CPU密集型线程池

文章目录 前言线程池各类场景描述常见场景案例设计思路公共类自定义工厂类-MyThreadFactory自定义拒绝策略-RejectedExecutionHandlerFactory自定义阻塞队列-TaskQueue&#xff08;实现 核心线程->最大线程数->队列&#xff09; 场景1&#xff1a;CPU密集型场景思路&…

使用开源项目:pdf2docx,让PDF转换为Word

目录 1.安装python 2.安装 pdf2docx 3.使用 pdf2docx 转换 PDF 到 Word pdf2docx&#xff1a;GitCode - 全球开发者的开源社区,开源代码托管平台 环境&#xff1a;windows电脑 1.安装python Download Python | Python.org 最好下载3.8以上的版本 安装时记得选择上&#…

蓝桥杯思维训练营(四)

文章目录 小红打怪494.目标和 小红打怪 小红打怪 思路分析&#xff1a;可以看到ai的范围较大&#xff0c;如果我们直接一个个进行暴力遍历的话&#xff0c;会超时。当我们的攻击的次数越大的时候&#xff0c;怪物的血量就会越少&#xff0c;这里就有一个单调的规律在里面&…

尝试把clang-tidy集成到AWTK项目

前言 项目经过一段时间的耕耘终于进入了团队开发阶段&#xff0c;期间出现了很多问题&#xff0c;其中一个就是开会讨论团队的代码风格规范&#xff0c;目前项目代码风格比较混乱&#xff0c;有的模块是驼峰&#xff0c;有的模块是匈牙利&#xff0c;后面经过讨论&#xff0c;…