Qt 基于FFmpeg的视频播放器 - 播放、暂停以及拖动滑动条跳转

Qt 基于FFmpeg的视频转换器 - 播放、暂停以及拖动进度条跳转

  • 引言
  • 一、设计思路
  • 二、核心源码以及相关参考链接

引言

效果展示 另存为gif

  • 本文基于FFmpeg,使用Qt制作了一个极简的视频播放器. 相比之前的版本,加入了播放暂停拖动滑动条跳转功能,如上所示 (左图):
  • 使用AVSEEK_FLAG_ANY可以精准跳转到某一帧,但会出现花屏 (左图).
  • 使用EV录屏,再使用本软件将其转为gif (左图),再GifCam截取本软件转gif的过程 (右图),GifCam无法截取鼠标.

可参考之前的博客:
Qt 基于FFmpeg的视频播放器 - QtFFmpegPlayer
Qt 基于FFmpeg的视频转换器 - 转GIF动图

一、设计思路

    1. 界面设计,鼠标移动到相应位置才会显示相关控件 (按钮、进度条),override鼠标移动事件
void QWidget_PlayVideo::mouseMoveEvent(QMouseEvent *event)
{
    // 布局在鼠标移动过程中会变化,使得布局内控件闪烁
//    if( m_Hlayout->geometry().contains(event->pos())){
//        for(int i = 0; i < m_Hlayout->count(); i++){
//            QLayoutItem *item = m_Hlayout->itemAt(i);
//            item->widget()->show();
//        }
//    }
//    else{
//        for(int i = 0; i < m_Hlayout->count(); i++){
//            QLayoutItem *item = m_Hlayout->itemAt(i);
//            item->widget()->hide();
//        }
//    }
    // 使用按钮和滑块的geometry进行判断:鼠标是否移动到窗口底部
    if( m_btn_startorstop->geometry().contains(event->pos()) ||
        m_slider->geometry().contains(event->pos())){
        for(int i = 0; i < m_Hlayout->count(); i++){
            QLayoutItem *item = m_Hlayout->itemAt(i);
            item->widget()->show();
        }
    }
    else{
        for(int i = 0; i < m_Hlayout->count(); i++){
            QLayoutItem *item = m_Hlayout->itemAt(i);
            item->widget()->hide();
        }
    }
}

建议不要使用布局的geometry,其在鼠标移动过程会变化 (暂不清楚为什么,可能bug 或者控件隐藏之后相关布局会变化,geometry也会随之改变) - 可优化/todo

  1. 初始化就记录下相对坐标,后续可以依据相对坐标判断.
  2. 按钮和进度条固定到最下方显示
    1. 开始和暂停功能,使用一个内部变量判断是否暂停
    connect(m_btn_startorstop, &QPushButton::clicked, this, [&]{    // 按钮点击,暂停 or 继续播放
        if(m_FFmpegVideo->m_stopPlay == false){
            qDebug()<<"视频暂停";
            m_FFmpegVideo->m_stopPlay = true;    // 停止运行,跳出循环
            // todo 修改按钮,播放
        }
        else{
            qDebug()<<"视频继续播放";
            m_FFmpegVideo->m_stopPlay = false;
            m_PlayThread->start();
            m_PlayThread->quit();   // 执行完后自动关闭,否则一直在运行中... 无法重新start发送开始信号
            // todo 修改按钮,暂停
        }
    });

暂停直接退出线程即可,avformat_context内部会记录进度,再播放会从下一帧继续解码 /todo 使用原子类型
可参考:QThread如何优雅实现暂停(挂起)功能

    1. 拖动进度条跳转
    connect(m_FFmpegVideo, &FFmpegVideo::sig_SendFrameNum_play, this, [&](int frame_id){  // 滑动条随视频播放滑动
        if(b_slidermoved == false){
            m_slider->setValue(frame_id);
        }
    });

    connect(m_slider, &QSlider::sliderReleased, this, [&]{     // 滑动条手动滑动,修改视频播放位置
        qDebug()<< "sliderReleased: " << m_slider->value();
        this->m_FFmpegVideo->JumptotheFrame(m_slider->value(), m_slider->value(), m_slider->value());
        this->m_FFmpegVideo->m_frame_id = m_slider->value();
        b_slidermoved = false;
    });

    connect(m_slider, &QSlider::sliderMoved, this, [&]{
        b_slidermoved = true;
    });

使用b_slidermoved判断滑动条是否被手动拖动,是的话就先停止滑动条随视频播放滑动. /todo 目前滑动条是根据帧id进行滑动,后续可以改为按照播放时间

void FFmpegVideo::JumptotheFrame(qint64 min_frame_id, qint64 frame_id, qint64 max_frame_id)
{  
    // 将帧号转换为时间戳
    int64_t min_ts = min_frame_id * this->m_frame_timestamp;
    int64_t ts = frame_id * this->m_frame_timestamp;
    int64_t max_ts = max_frame_id * this->m_frame_timestamp;

    qDebug()<<"跳转到:" << ts/1000000.0 << "s";
    // this->av_stream_index
    // avformat_seek_file(this->avformat_context, -1, min_ts, ts, max_ts, AVSEEK_FLAG_FRAME);
    avformat_seek_file(this->avformat_context, -1, min_ts, ts, max_ts, AVSEEK_FLAG_ANY);
}

由于传递的参数是第几帧,需将帧号转为视频时间戳
使用AVSEEK_FLAG_ANY可以精准跳转到某一帧,但会出现花屏
使用AVSEEK_FLAG_FRAME不会出现花屏,但是无法精准跳转某帧,只会跳转到视频关键帧

  • /todo 还有很多待优化的bug…

二、核心源码以及相关参考链接

    1. 全部源码

已在gitee开源:QtFFmpegPlayerDemo

    1. 相关参考链接

【Qt+FFmpeg】解码播放本地视频(二)——实现播放、暂停、重播、倍速功能
【FFmpeg+Qt】视频进度条控制——点击跳转和拖动跳转
FFmpeg源码分析:av_seek_frame()与avformat_seek_file()
avformat_seek_file函数介绍
FFmpeg中的时间基(time_base), AV_TIME_BASE
ffmpeg协议之接口篇之快进快退(av_seek_frame)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙开发Ability Kit(程序框架服务):【ServiceAbility切换】 组件切换

ServiceAbility切换 FA模型中的ServiceAbility对应Stage模型中的ServiceExtensionAbility。Stage模型下的ServiceExtensionAbility为系统API&#xff0c;只有系统应用才可以创建。因此&#xff0c;FA模型的ServiceAbility的切换&#xff0c;对于系统应用和三方应用策略有所不同…

喜讯!恒瑞医药荣获2023年度国家科技进步奖

6月24日&#xff0c;2023年度国家科学技术奖在京公布&#xff0c;恒瑞医药参与、由山东第一医科大学附属肿瘤医院牵头的“肺癌放疗联合分子靶向和免疫治疗的关键机制与临床应用”项目荣获国家科学技术进步二等奖。这是公司继2016年度、2017年度获奖之后第3次获得国家科技进步奖…

ONLYOFFICE8.1新版本桌面编辑器测评

什么是 ONLYOFFICE 文档 ONLYOFFICE 文档是一套功能强大的文档编辑器&#xff0c;支持编辑处理文本文档、电子表格、演示文稿、可填写的表单、PDF&#xff0c;可多人在线协作&#xff0c;支持 AI 集成。 该套件可在 Windows、Linux、Android 和 iOS上使用&#xff0c;包括网页…

二级web基础操作题练习

---------要求--------- 利用HTML和CSS实现如图所示页面&#xff1a; ---------代码示例--------- 分析&#xff1a;该页面包含一个标题、一个副标题、“姓名信息”的表格&#xff0c;并且有一段文字提示用户仔细填写&#xff0c;使用内联CSS来控制HTML页面的视觉外观&…

bable 【实用教程】

简介 bable 用于将 ES6 的语法编译为 ES5 只关心语法&#xff0c;不关心 API 是否正确。不处理模块化&#xff08;webpack 会处理&#xff09; 搭建开发环境 安装相关的包 npm i babel/cli babel/core babel/preset-env新建文件 .babelrc&#xff0c;内容为 { "presets…

策略模式和状态模式

策略模式 在上下文中携带策略接口作为成员变量&#xff0c;在使用上下文之前需要设置策略setStrategy&#xff08;&#xff09;&#xff0c;然后使用策略接口成员变量来进行策略的执行。 步骤1&#xff1a;定义策略接口 // 策略接口 public interface Strategy {int execut…

酒店民宿预订小程序:高效管理,便捷入住

目前&#xff0c;我国旅游业发展非常旺盛&#xff0c;同时也带动了酒店民宿的快速发展。随着互联网科技的快速发展&#xff0c;酒店民宿小程序层出不穷。一个高效的小程序目前已经成为了各大服务业中必不可少的环节&#xff0c;对于酒店民宿商家来说&#xff0c;线上小程序不仅…

Day.js

Day.js 是什么&#xff1f; Day.js是一个极简的JavaScript库&#xff0c;可以为现代浏览器解析、验证、操作和显示日期和时间。 Day.js中文网 为什么要使用Day.js &#xff1f; 因为Day.js文件只有2KB左右&#xff0c;下载、解析和执行的JavaScript更少&#xff0c;为代码留下更…

2024/5/9【贪心5/5】--代码随想录算法训练营day36|56. 合并区间、738.单调递增的数字、968.监控二叉树 (可跳过)

56. 合并区间 力扣链接 class Solution:def merge(self, intervals):result []if len(intervals) 0:return result # 区间集合为空直接返回intervals.sort(keylambda x: x[0]) # 按照区间的左边界进行排序result.append(intervals[0]) # 第一个区间可以直接放入结果集中…

Java 树形结构数据如何高效返回给前端进行展示?

在开发过程中我们总是遇到一些具有层次结构的数据&#xff0c;这些数据在前端也总是需要以树形结构进行显示&#xff0c;那么后端接口如何高效的去将这些数据封装成树形结构呢&#xff1f;下面来进行解析讲解。 最终实现的一个结果图 设计返回的实体VO import com.fasterxm…

58.鸿蒙系统app(HarmonyOS)(ArkUI)更改应用程序图标

替换xx\MyApplication4.30\entry\src\main\resources\base\media目录下icon.png文件 54.HarmonyOS鸿蒙系统 App(ArkTS)tcp socket套接字网络连接收发测试_鸿蒙socket连接测试-CSDN博客

windows远程桌面你会了吗?

1、当你发现正常连接无法连接时&#xff1f; 试试以管理员身份连接 mstsc /admin /v:IP 2、当本机与远程桌面分辨率不一致时? 指定分辨率连接&#xff0c;如1920*1080 mstsc /w:1920 /h:1080 /v:IP 适应本机分辨率连接 mstsc /span /v:IP 3、当远程连接的端口不是3389…

【设计模式-04】原型模式

【设计模式-04】原型模式 1. 概述2. 结构3. 实现4. 案例5. 使用场景6. 优缺点6.1 原型模式的优点6.2 原型模式的缺点 7. 实现深克隆(深拷贝) 1. 概述 原型模式: 用一个已经创建的实例作为原型&#xff0c;通过复制该原型对象来创建一个和原型对象相同的新对象。 2. 结构 原型…

VSCode常用的一些插件

Chinese (Simplified) 汉语&#xff08;简体&#xff09;拓展包。 Auto Close Tag 可以自动增加xml/html的闭合标签。 CodeSnap 截图神器。截图效果在下面。 Dracula Official vscode一个很好看的主题。 Git Graph git管理工具。 GitHub Repositories 有了它&#xff0c;不…

基于Vue+ElementUI框架实现学生管理系统前端页面设计

目录 一. 最终效果展示 二. 详细教程 1. 创建项目 2. 下载组件 3. 在main.js中配置 4. 创建项目中的组件(页面) 登录组件 Login.vue 系统主页组件 Main.vue 学生管理组件 StudentList.vue 专业管理组件 MajorList.vue 5. 在index.js中配置组件路由 6. 添加画布 三…

【PyQt5】一文向您详细介绍 QVBoxLayout() 的作用

【PyQt5】一文向您详细介绍 QVBoxLayout() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&a…

算法金 | 没有思考过 Embedding,不足以谈 AI

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 抱个拳&#xff0c;送个礼 在当今的人工智能&#xff08;AI&#xff09;领域&#xff0c;Embedding 是一个不可或缺的概念。如果你没有…

第六篇:精通Docker Compose:打造高效的多容器应用环境

精通Docker Compose&#xff1a;打造高效的多容器应用环境 1. 引言 1.1 目的与重要性 在现代软件开发中&#xff0c;随着应用程序的复杂性不断增加&#xff0c;传统的单一容器部署方式已无法满足需求。Docker Compose作为一种强大的工具&#xff0c;专门用于定义和运行多容器…

用户中心项目全流程

企业做项目流程 需求分析 > 设计&#xff08;概要设计 、 详细设计&#xff09; > 技术选型 >初始化项目 / 引入需要的技术 > 写个小demo > 写代码 &#xff08;实现业务逻辑&#xff09; > 测试&#xff08;单元测试&#xff09;> 代码提交 / 代码评审 …

【C++:list】

list概念 list是一个带头的双向循环链表&#xff0c;双向循环链表的特色&#xff1a;每一个节点拥有两 个指针进行维护&#xff0c;俩指针分别为prev和next,prev指该节点的前一个节点&#xff0c;next为该节点的后一个节点 list的底层实现中为什么对迭代器单独写一个结构体进行…