当前位置: 首页 > article >正文

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

一、前言说明

音视频开发除了应用在安防监控、视频网站、各种流媒体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.kler.cn/a/532306.html

相关文章:

  • VLAN 基础 | 不同 VLAN 间通信实验
  • 安全策略实验报告
  • python:如何播放 .spx 声音文件
  • 99,[7] buuctf web [羊城杯2020]easyphp
  • 基于Kamailio、MySQL、Redis、Gin、Vue.js的微服务架构
  • Rust中使用ORM框架diesel报错问题
  • win32汇编环境,对话框程序生成选项卡(属性页\标签)控件及运用
  • swagger使用指引
  • 网站快速收录:如何优化网站H标签使用?
  • 【操作系统】同步与异步,同步与互斥
  • 【学习笔记】计算机图形学的几何数学基础知识
  • 【Redis】主从模式,哨兵,集群
  • 每日一题——小根堆实现堆排序算法
  • 低通滤波算法的数学原理和C语言实现
  • vim-plug的自动安装与基本使用介绍
  • 【学术征稿-组织单位 武汉理工大学西安理工大学、西安财经大学】第三届通信网络与机器学习(CNML 2025)
  • Codeforces Round 1002 (Div. 2)(部分题解)
  • 利用Python高效处理大规模词汇数据
  • MongoDB 聚合
  • 简易CPU设计入门:指令单元(三)
  • 【Numpy核心编程攻略:Python数据处理、分析详解与科学计算】2.29 NumPy+Scikit-learn(sklearn):机器学习基石揭秘
  • DeepSeek蒸馏模型:轻量化AI的演进与突破
  • 测试csdn图片发布
  • 为何在Kubernetes容器中以root身份运行存在风险?
  • 机器学习在环境科学中的应用
  • BUU16 [ACTF2020 新生赛]BackupFile1