【三十八】【QT开发应用】vlcplayer视频播放器(一)实现视频播放,视频暂停,视频停止,进度条调节,音量调节,时长显示功能

效果展示

vlcplayer_test视频播放器

MainWidget.ui

在这里插入图片描述

  • 注意控件的布局和命名,控件的命名和信号与槽函数的绑定有关,所以这点很重要。

下载VLC组件和环境配置

  • videolan下载地址
  • 我下载的是vlc-3.0.8-win64版本.
    在这里插入图片描述
    在这里插入图片描述
  • 将下载的文件复制粘贴到项目文件中.
    在这里插入图片描述
    在这里插入图片描述
  • 复制粘贴这三个文件到项目的debug文件夹里面.
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  • 然后我们在主文件里面添加#include <vlc/vlc.h>头文件.

MainWidget.h

#pragma once#include <QtWidgets>
#include "ui_MainWidget.h"
#include <vlc/vlc.h>class MainWidget : public QWidget
{Q_OBJECTpublic:MainWidget(QWidget *parent = nullptr);~MainWidget();libvlc_media_player_t* getMediaPlayer();//获取media player对象qint64 getDuration();//获取总时长,单位是秒void setTimeSliderPos(int value);//设置时间滑动条的值void setVolumeSliderPos(int value);//设置音量滑动条的值void setTimeText(QString str);//设置时间标签文本private slots:void on_btnOpen_clicked();//打开按钮槽函数void on_btnPlay_clicked();//播放按钮槽函数void on_btnPause_clicked();//暂停按钮槽函数void on_btnStop_clicked();//停止按钮槽函数void onTimeSliderMoved(int value);//时间滑动条移动槽函数void onVolumeSliderMoved(int value);//音量滑动条移动槽函数
private:Ui::MainWidgetClass ui;libvlc_instance_t* m_pInstance = nullptr;//视频示例,负责管理全局配置和资源libvlc_media_player_t* m_pMediaPlayer = nullptr;//VLC 媒体播放器的指针,用于控制媒体的播放、暂停、停止等操作libvlc_media_t* m_pMedia = nullptr;// VLC 媒体的指针,用于加载和管理特定的媒体内容(如音频或视频文件)libvlc_event_manager_t* m_pEvent_manager = nullptr;//事件管理器的指针,负责处理 VLC 播放器的各种事件(例如媒体播放完毕、音量变化等)qint64 m_totalSecs = -1;//总时长,单位是秒
};
  • 我们利用vlc库去实现视频播放器,那么我们就需要按照他人的要求去编写代码,因为vlc库是别人写出来的东西,而他人的要求就是需要我们去定义示例,媒体播放器,媒体,事件等对象,并按照要求去使用.

MainWidget.cpp

#include "MainWidget.h"void vlc_callback(const struct libvlc_event_t* p_event, void* p_data) {MainWidget* pMain = static_cast<MainWidget*>(p_data);if (pMain) {switch (p_event->type) {case libvlc_MediaPlayerPositionChanged:{float pos = libvlc_media_player_get_position(pMain->getMediaPlayer());pMain->setTimeSliderPos(pos * 100);qint64 curSecs = libvlc_media_player_get_time(pMain->getMediaPlayer()) / 1000;int curH = curSecs / 3600;int curMinute = (curSecs - curH * 3600) / 60;int curSec = curSecs - curH * 3600 - curMinute * 60;QString str1 = QString("%1:%2:%3").arg(curH, 2, 10, QChar('0')).arg(curMinute, 2, 10, QChar('0')).arg(curSec, 2, 10, QChar('0'));qint64 totalSecs = pMain->getDuration();int totalH = totalSecs / 3600;int totalMinute = (totalSecs - totalH * 3600) / 60;int totalSec = totalSecs - totalH * 3600 - totalMinute * 60;QString str2 = QString("%1:%2:%3").arg(totalH, 2, 10, QChar('0')).arg(totalMinute, 2, 10, QChar('0')).arg(totalSec, 2, 10, QChar('0'));QString text = str1 + "/" + str2;pMain->setTimeText(text);break;}case libvlc_MediaPlayerAudioVolume:{int volume = libvlc_audio_get_volume(pMain->getMediaPlayer());pMain->setVolumeSliderPos(volume);break;}}}
}MainWidget::MainWidget(QWidget* parent): QWidget(parent) {ui.setupUi(this);m_pInstance = libvlc_new(0, nullptr);if (m_pInstance) {m_pMediaPlayer = libvlc_media_player_new(m_pInstance);if (m_pMediaPlayer) {m_pEvent_manager = libvlc_media_player_event_manager(m_pMediaPlayer);libvlc_event_attach(m_pEvent_manager, libvlc_MediaPlayerPositionChanged, vlc_callback, this);libvlc_event_attach(m_pEvent_manager, libvlc_MediaPlayerAudioVolume, vlc_callback, this);} else {libvlc_release(m_pInstance);QMessageBox::information(this, "提示", "libvlc_media_player_new failed");exit(EXIT_FAILURE);}} else {QMessageBox::information(this, "提示", "libvlc_new failed");exit(EXIT_FAILURE);}ui.label_time->setText("");connect(ui.timeSlider, &QSlider::sliderMoved, this, &MainWidget::onTimeSliderMoved);connect(ui.volumeSlider, &QSlider::sliderMoved, this, &MainWidget::onVolumeSliderMoved);
}MainWidget::~MainWidget() {if (m_pMediaPlayer) {libvlc_media_player_release(m_pMediaPlayer);m_pMediaPlayer = nullptr;}if (m_pInstance) {libvlc_release(m_pInstance);m_pInstance = nullptr;}}libvlc_media_player_t* MainWidget::getMediaPlayer() {return m_pMediaPlayer;
}qint64 MainWidget::getDuration() {return m_totalSecs;
}void MainWidget::setTimeSliderPos(int value) {ui.timeSlider->setValue(value);
}void MainWidget::setVolumeSliderPos(int value) {ui.volumeSlider->setValue(value);
}void MainWidget::setTimeText(QString str) {ui.label_time->setText(str);
}void MainWidget::on_btnOpen_clicked() {QString filename = QFileDialog::getOpenFileName(this, "请选择视频文件", "D:","视频文件(*.mp4 *.flv);;所有文件(*.*);;");if (filename.isEmpty()) return;filename = QDir::toNativeSeparators(filename);m_pMedia = libvlc_media_new_path(m_pInstance, filename.toStdString().c_str());if (m_pMedia) {libvlc_media_parse(m_pMedia);libvlc_media_player_set_media(m_pMediaPlayer, m_pMedia);libvlc_media_player_set_hwnd(m_pMediaPlayer, (void*)(ui.video_widget->winId()));libvlc_media_release(m_pMedia);m_totalSecs = libvlc_media_get_duration(m_pMedia) / 1000;m_pMedia = nullptr;libvlc_media_player_play(m_pMediaPlayer);} else {if (m_pMediaPlayer) {libvlc_media_player_release(m_pMediaPlayer);m_pMediaPlayer = nullptr;}if (m_pInstance) {libvlc_release(m_pInstance);m_pInstance = nullptr;}QMessageBox::information(this, "提示", "libvlc_media_new_path failed");exit(EXIT_FAILURE);}}void MainWidget::on_btnPlay_clicked() {if (libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Paused|| libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Stopped) {libvlc_media_player_play(m_pMediaPlayer);}
}void MainWidget::on_btnPause_clicked() {if (libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Playing) {libvlc_media_player_pause(m_pMediaPlayer);}
}void MainWidget::on_btnStop_clicked() {if (libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Playing|| libvlc_media_player_get_state(m_pMediaPlayer) == libvlc_state_t::libvlc_Paused) {libvlc_media_player_stop(m_pMediaPlayer);}
}void MainWidget::onTimeSliderMoved(int value) {libvlc_media_player_set_position(m_pMediaPlayer, value / 100.0);
}void MainWidget::onVolumeSliderMoved(int value) {libvlc_audio_set_volume(m_pMediaPlayer, value);
}

MainWidget

  • m_pInstance = libvlc_new(0, nullptr);利用libvlc_new函数去创建示例对象.
  • m_pMediaPlayer = libvlc_media_player_new(m_pInstance);如果实例创建成功,就创建视频媒体对象,利用libvlc_media_player_new()函数创建对象.
  • m_pEvent_manager = libvlc_media_player_event_manager(m_pMediaPlayer);如果视频媒体对象创建成功,就创建事件管理器的对象,利用libvlc_media_player_event_manager()函数创建对象.
  • libvlc_event_attach(m_pEvent_manager, libvlc_MediaPlayerPositionChanged, vlc_callback, this);视频媒体位置改变了就会发送信号到回调函数vlc_callback也就是说只要视频在播放,那么就会持续不断的发送信号到回调函数`vlc_callback中.
  • libvlc_event_attach(m_pEvent_manager, libvlc_MediaPlayerAudioVolume, vlc_callback, this);视频声音发生改变的时候触发的事件,发送信号到回调函数vlc_callback,这段代码的意义是保证程序一进去的时候音量和滑动条是对应的.
  • connect(ui.timeSlider, &QSlider::sliderMoved, this, &MainWidget::onTimeSliderMoved);时间滑动条被拖动的时候发送信号触发槽函数.
  • connect(ui.volumeSlider, &QSlider::sliderMoved, this, &MainWidget::onVolumeSliderMoved);音量滑动条被拖动的时候发送信号触发槽函数.

vlc_callback

  • void vlc_callback(const struct libvlc_event_t* p_event, void* p_data) 这是回调函数的固定写法.p_event 是指向 libvlc_event_t 类型的常量指针,表示发生的具体事件.在回调函数中,通常将 p_data 转换为具体类型(例如指向 MainWidget 的指针),以便在回调中访问类成员或调用类方法。
  • MainWidget* pMain = static_cast<MainWidget*>(p_data);p_date进行安全的类型转换,转换成MainWidget类型,这样我们就可以通过pMain直接访问主窗口类里面的对象成员.
  • 利用switch语句对传递进来的不同事件进行不同的代码处理,我们只编辑了两个具体的事件,一个是libvlc_MediaPlayerPositionChanged事件,视频只要在播放就会一直发送信号,一个是libvlc_MediaPlayerAudioVolume,视频的音量改变了就会发送信号,用于进入程序时将音量的滑动条和音量进行绑定.
  • libvlc_MediaPlayerAudioVolume内部代码,
    int volume = libvlc_audio_get_volume(pMain->getMediaPlayer());获取VLC播放器的音量,volume 的值通常在 0 到 100 之间,表示音量的百分比.然后
    pMain->setVolumeSliderPos(volume);设置这个音量到音量滑动条中.也就是说我们一进入程序获取音量然后设置音量滑动条的值.之后我们手动改变音量滑动条视频音量和滑动条通过槽函数一直都是绑定在一起的.
  • libvlc_MediaPlayerPositionChanged内部代码,
    float pos = libvlc_media_player_get_position(pMain->getMediaPlayer());获取视频当前进度占总进度的百分比值,返回值时一个0~1的float类型的值,
    pMain->setTimeSliderPos(pos * 100);将返回值乘以100得到的是具体的值,因为事件滑动条默认是0~100,这样我们直接设置乘以100的值为此时滑动条的值就按照百分比绑定了当前视频进度和滑动条位置.
  • qint64 curSecs = libvlc_media_player_get_time(pMain->getMediaPlayer()) / 1000;获取当前视频的时间,单位是毫秒,转化为秒需要除以1000.然后按照计算得到此时对应的时分秒.将时分秒转化为对应的字符串,然后计算总时间的时分秒转化为字符串拼接在一起,设置为Label的text值.

on_btnOpen_clicked

  • 打开按钮需要实现的效果是,点击之后需要有一个弹窗,选择我们需要播放的视频文件,然后播放.
  • getOpenFileName函数实现弹窗选择视频文件的功能,toNativeSeparators函数将得到的路径转换为正确的格式路径,因为得到的路径 \和/可能不符合当前程序QT里面的规则,用这个函数可以进行转化统一.
  • m_pMedia = libvlc_media_new_path(m_pInstance, filename.toStdString().c_str());得到视频的路径之后创建媒体的对象.
  • libvlc_media_parse(m_pMedia);解析媒体文件,以便从文件中提取元数据信息(如时长、标题、格式等)。
    libvlc_media_player_set_media(m_pMediaPlayer, m_pMedia);将媒体 m_pMedia 设置到媒体播放器 m_pMediaPlayer 中。此操作指示播放器应使用该媒体文件作为播放内容。此时 m_pMedia 已经和播放器绑定,不再需要手动管理其生命周期。
    libvlc_media_player_set_hwnd(m_pMediaPlayer, (void*)(ui.video_widget->winId()));设置视频输出窗口的句柄,使播放器的视频内容渲染在指定的窗口控件上。此处 ui.video_widget->winId() 获取了窗口句柄,将其转换为 void* 类型。
  • m_totalSecs = libvlc_media_get_duration(m_pMedia) / 1000;在媒体进行销毁之前计算出视频总时长,得到的时间是毫秒单位需要除以1000转化为秒.

on_btnPlay_clicked等其他按钮

  • ibvlc_media_player_get_state(m_pMediaPlayer)得到当前的状态,libvlc_state_t::libvlc_Paused暂停状态,libvlc_state_t::libvlc_Stopped停止状态,libvlc_state_t::libvlc_Playing播放状态,
  • libvlc_media_player_play(m_pMediaPlayer);播放视频媒体,libvlc_media_player_pause(m_pMediaPlayer);暂停视频媒体,libvlc_media_player_stop(m_pMediaPlayer);停止视频媒体

杂项

  • libvlc_media_player_set_position设置视频媒体时长位置,
    value / 100.0:value 通常是从进度滑块控件中获取的整数值,范围通常是 0 到 100。通过除以 100.0,将其转换为 0.0 到 1.0 之间的小数(即百分比),符合 libvlc 所需的参数范围。0.0 表示播放开始位置,1.0 表示播放结束位置。
  • libvlc_audio_set_volume(m_pMediaPlayer, value);
    value:要设置的音量值,通常范围是 0 到 100,0 表示静音;100 表示最大音量(也可以设置大于 100,视 VLC 配置可能允许更高的音量,但通常建议限制在 0-100 范围内)。

在这里插入图片描述

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。
同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。
谢谢您的支持,期待与您在下一篇文章中再次相遇!

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

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

相关文章

【LeetCode每日一题】——1791.找出星型图的中心节点

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时空频度】九【代码实现】十【提交结果】 一【题目类别】 图 二【题目难度】 简单 三【题目编号】 1791.找出星型图的中心节点 四【题目描述】 有一个…

深入了解 美国高防 CN2 :如何提升全球化业务的网络安全与性能

美国高防 CN2 的重要性 在跨国企业和全球化业务的不断扩展下&#xff0c;对高性能和安全的网络连接需求不断增加。美国高防 CN2&#xff08;Global Internet Access&#xff09;以其卓越的跨境传输效率和强大的防护能力&#xff0c;成为许多企业关注的焦点。尤其是对电商、游戏…

Endnote如何关联Word,在Word上通过Endnote插入文献

1、Word版本已被激活 首先检查Word版本是否已经被激活&#xff0c;不管是正版还是破解版&#xff0c;未激活状态&#xff08;试用&#xff09;可能会导致关联不成功。如下图所示为已激活版本&#xff0c;在“文件”—“账户”可看。 2、勾选开发工具 其次&#xff0c;打开Wor…

1688旺铺主页首页装修 代码 首页悬浮视频怎么做?阿里巴巴代码装修悬浮视频代码固定视频悬浮漂浮视频

1688旺铺主页首页装修 代码 首页悬浮视频怎么做&#xff1f;阿里巴巴代码装修悬浮视频代码固定视频悬浮漂浮视频 一秒美工工具

【错误描述:“L2TP连接尝试失败,因为安全层在初始化与远程计算机的协商时遇到了一个处理错误”】

解决办法&#xff1a; 一、检查并更改网络协议 &#xff08;如果网络协议更改完成&#xff0c;还是链接失败&#xff0c;直接看 第二点&#xff09; 1、打开网络和Internet 设置 2、找到更改适配器选项 3、先择你要链接VPN&#xff0c;右键选择属性&#xff0c;之后选择安…

基于SSM学生竞赛模拟系统的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;用户管理&#xff0c;公告信息管理&#xff0c;试题管理&#xff0c;论坛交流&#xff0c;试卷管理&#xff0c;系统管理 前台账号功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;公告…

罗马仕、西圣、安克充电宝哪款品牌更好?综合测评对比谁是TOP.1

罗马仕、西圣、安克充电宝哪款品牌更好&#xff1f;在移动设备日益普及的今天&#xff0c;充电宝已成为许多人的必备配件。市场上涌现出多个知名品牌的充电宝&#xff0c;其中罗马仕、西圣和安克尤为受到消费者的关注。这三个品牌在产品质量、技术创新和用户体验方面各有千秋&a…

spring boot + mybatis + mysql 批量更新报错

问题描述&#xff1a;spring boot mybatis mysql 批量更新报错&#xff0c;如下图所示&#xff1a; mapper文件如下&#xff1a; <!--更新样品项目结果/检验结果内容&#xff0c;提交结果--><update id"submitResult" parameterType"com.weiyu.pojo…

Native Crash 信号速查

重点信号 SIGSEGV&#xff08;11&#xff09; signal segmentation violation&#xff1a;段错误无效内存访问访问无权访问的内存空指针、越界访问&#xff08;mmap&#xff1f;&#xff09; SIGBUS&#xff08;7&#xff09; Bus Error&#xff1a;总线错误非法内存访问访问 …

mac-ubuntu虚拟机(扩容-共享-vmtools)

一、磁盘扩容 使用GParted工具对Linux磁盘空间进行扩展 https://blog.csdn.net/Time_Waxk/article/details/105675468 经过上面的方式后还不够&#xff0c;需要再进行下面的操作 lvextend 用于扩展逻辑卷的大小&#xff0c;-l 选项允许指定大小。resize2fs 用于调整文件系统的…

SQL进阶技巧:巧用异或运算解决经典换座位问题

目录 0 问题描述 1 数据准备 2 问题分析 2.1 什么是异或 2.2异或有什么特性? 2.3 异或应用 2.4 本问题采用异或SQL解决方案 3 小结 0 问题描述 表 seat中有2个字段id和student id 是该表的主键(唯一值)列,student表示学生姓名。 该表的每一行都表示学生的姓名和 ID。…

质数的小游戏~(牛客,cf)

添加链接描述 H 题&#xff1a; n 的范围是 1e6 大致的思路 就是 每一段 固定一个质数&#xff0c;然后这一段中的 数下标 的和都是这个质数。 对于[1 n] 这些数 &#xff0c;对于n 向前找到 一个比他大的最小的质数。假设这个质数nj 。那么也就是说 我n 这个数应该放在下标为…

【热门主题】000018 人工智能深度学习模型:探索与应用

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 【热…

Linux学习_8

第七章命令解释器-shell 主要包括Linux中特殊符号&#xff0c;命令别名&#xff0c;命令历史 概述&#xff1a; shell&#xff1a;壳&#xff0c;命令解释器&#xff0c;负责解析用户输入的命令 type命令&#xff1a; 查看命令是内置命令、外置命名、alias命令 type -参数 命…

C++从入门到起飞之——红黑树封装map和set 全方位剖析!

目录 1、map和set的整体框架 2、map和set迭代器的实现 3、map支持[] 4、完整源码 set.h map.h RBTree.h 1、map和set的整体框架 因为map和set的底层都是红黑树&#xff0c;所以我们考虑用一个红黑树的类模版去实例化map和set对象&#xff01;不过&#xff0c;map节点中存…

用Pyhon写一款简单的益智类小游戏——2048

文字版——代码及讲解 代码—— import random# 初始化游戏棋盘 def init_board():return [[0] * 4 for _ in range(4)]# 在棋盘上随机生成一个2或4 def add_new_tile(board):empty_cells [(i, j) for i in range(4) for j in range(4) if board[i][j] 0]if empty_cells:i,…

git仓库分支

操作 切换分支 git checkout 1.2.5 git checkout 1.3.0 使用命令切换分支之后&#xff0c;代码内容加载过后也是切换好的

突破挑战,创新前行 | 生信科技SOLIDWORKS 2025新品发布会·合肥站精彩回顾

2024年10月18日&#xff0c;由生信科技举办的首场SOLIDWORKS 2025新产品发布会在安徽合肥圆满落幕。现场邀请到制造业的专家学者们一同感受SOLIDWORKS 2025最新功能&#xff0c;探索制造业数字化转型之路。 合肥站活动日&#xff0c;由生信科技副总经理徐建开场。他以智造无界&…

《C Primer Plus》中文版第十六章习题

16.17 复习题 1. 下面的几组代码由一个或多个宏组成&#xff0c;其后是使用宏的源代码。在每种情况下代码的结果是什么?这些代码是否是有效代码?(假设其中的变量已声明)。 a. #define FPM 5280 dist FPM * miles; b. #define FEET 4 #define POD FEET FEET plort …

Linux·进程间通讯(管道)

从本节开始将使用vscode写代码&#xff0c;语言也切换成C&#xff0c;同时OS从centOS换成ubentu。 进程之间可能存在数据传输、资源共享、通知事件、进程控制等需求&#xff0c;但是进程又具有独立性&#xff0c;所以就需要专门的进程间通讯技术(ipc)来满足需求。进程间通讯(IP…