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

news2024/12/24 22:07:48

效果展示

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_OBJECT

public:
    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.coloradmin.cn/o/2229194.html

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

相关文章

5G NR NARFCN计算SSB中心频率MATLAB实现

本期给大家带来5G NR中已知绝对射频信道号NARFCN如何计算SSB的中心频率&#xff0c;用MATLAB实现&#xff0c;参考3GPP 38.104 下图是NARFCN与SSB中心频率换算关系&#xff0c;其中NREF就是NARFCN。 函数输出频率的单位是MHZ&#xff0c;输入是NARFCN。 有不清楚的地方欢迎来…

auto 项目笔记

基础设置 1.设置python目录为根路径 1. merge_with_history debug (1) coomon.yaml 修改 最下边的 root: /mnt/sdb/daimler/EHPV2/Ruiming_InternalDaily (2) 环境变量设置为1时 不走此步骤做的任何处理&#xff0c;直把上步数据接透传出去 2.

深度学习(九):推荐系统的新引擎(9/10)

一、深度学习与推荐系统的融合 深度学习在推荐系统中的融合并非偶然。随着互联网的飞速发展&#xff0c;数据量呈爆炸式增长&#xff0c;传统推荐系统面临着诸多挑战。例如&#xff0c;在处理大规模、高维度的数据时&#xff0c;传统方法往往显得力不从心。而深度学习以其强大的…

如何建立测试团队的知识库

后台有同学留言&#xff0c;问了这样一个问题&#xff1a; 刚担任测试负责人&#xff0c;团队流程很乱&#xff0c;需求不甚明确&#xff0c;测试工作基本靠员工的熟练程度&#xff0c;项目交付质量堪忧。 迭代排期紧张&#xff0c;新招来的同学没人带&#xff0c;也没有文档之…

Spring 框架环境搭建

一、环境要求 JDK版本&#xff1a; JDK1.7及以上版本 Spring版本&#xff1a; Spring5.x版本 二、新建Maven项目 1. 创建 Maven 的普通 Java 项⽬ 2.设置项目坐标 3.设置项目的Maven环境 4.设置项目的名称和存放的工作空间 三、调整项目环境 1.修改JDK版本 properties&g…

[Wireshark] 使用Wireshark抓包https数据包并显示为明文、配置SSLKEYLOGFILE变量(附下载链接)

前言 wireshark安装包 链接&#xff1a;https://pan.quark.cn/s/febb28f57c01 提取码&#xff1a;fUCQ 链接失效&#xff08;可能会被官方和谐&#xff09;可评论或私信我重发 chrome与firefox在访问https网站的时候会将密钥写入这个环境变量SSLKEYLOGFILE中&#xff0c;在wir…

高考相关 APP 案例分享

文章首发于https://qdgithub.com/article/2032 一、核心内容 &#xff08;一&#xff09;高考相关 APP 案例 圈友朱康分享高考相关的 APP。提到猿题库&#xff0c;其主要功能有练习册和猿辅导&#xff0c;都是收费的。猿题库出题给学生练习&#xff0c;将易错的总结起来出练习…

【postman】工具下载安装

postman作用 postman用于测试http协议接口&#xff0c;无论是开发, 还是测试人员, 都有必要学习使用postman来测试接口, 用起来非常方便。 环境安装 postman 可以直接在chrome 上安装插件&#xff0c;当然大部分的同学是没法连接到谷歌商店的&#xff0c;我们可以在电脑本地…

【C++】 string的了解及使用

标准库中的string类 在使用string类时&#xff0c;必须包含#include头文件以及using namespace std; string类的常用接口说明 C中string为我们提供了丰富的接口来供我们使用 – string接口文档 这里我们只介绍一些常见的接口 string类对象的常见构造 #include <iostrea…

守护头顶安全——AI高空抛物监测,让悲剧不再重演

在城市的喧嚣中&#xff0c;我们享受着高楼林立带来的便捷与繁华&#xff0c;却往往忽视了那些隐藏在高空中的危险。近日&#xff0c;震惊全国的高空抛物死刑案件被最高院核准并执行。案件中被告人多次高空抛物的举动&#xff0c;夺去了无辜者的生命&#xff0c;也让自己付出了…

好消息:貌似稳定盈利了;坏消息:发财无望

声明&#xff1a;股市有风险&#xff0c;入市需谨慎。所有材料仅供学习参考&#xff0c;不构成投资建议。 作者&#xff1a;NazariteBruce 事实上&#xff0c;从8月份就开始在测试自己的量化系统&#xff0c;连续三个月赚钱了&#xff0c;虽然赚的不多&#xff0c;但是也验证了…

【Linux网络】TCP_Socket

目录 TCP协议&#xff08;传输控制协议&#xff09; listen状态 accept和connect TCP_echo_server (1)创建套接字 &#xff08;2&#xff09;绑定 &#xff08;3&#xff09;设置listen状态 &#xff08;4&#xff09;loop &#xff08;5&#xff09;客户端 多线程远程…

算法|牛客网华为机试11-20C++

牛客网华为机试 上篇&#xff1a;算法|牛客网华为机试1-10C 文章目录 HJ11 数字颠倒HJ12 字符串反转HJ13 句子逆序HJ14 字符串排序HJ15 求int型正整数在内存中存储时1的个数HJ16 购物单HJ17 坐标移动HJ18 识别有效的IP地址和掩码并进行分类统计HJ19 简单错误记录HJ20 密码验证…

Pandas DataFrame学习补充

1. 从字典创建&#xff1a;字典的键成为列名&#xff0c;值成为列数据。 import pandas as pd# 通过字典创建 DataFrame df pd.DataFrame({Column1: [1, 2, 3], Column2: [4, 5, 6]}) 2. 从列表的列表创建&#xff1a;外层列表代表行&#xff0c;内层列表代表列。 df pd.Da…

剖析高精度、直线电机技术八大常见问题你了解吗?

在现代工业自动化和机械工程的快速发展背景下&#xff0c;高精度直线电机技术逐渐崭露头角&#xff0c;成为推动各类高端设备和系统的重要力量。直线电机以其独特的工作原理和优越的性能特点&#xff0c;广泛应用于数控机床、自动化生产线、机器人等领域。 一、什么是高精度直线…

如何解决RabbitMQ消息的重复消费问题

什么情况下会导致消息的重复消费——在消费者还没成功发送自动确认机制时发生&#xff1a; 网络抖动消费者挂了 解决方案 每条消息设置一个唯一的标识id幂等方案&#xff1a;【Redis分布式锁、数据库锁&#xff08;悲观锁、乐观锁&#xff09;】 面试官&#xff1a;如何解决…

jade 0919 | 提取自TVBox的直播盒子,频道丰富高清

jade电视直播app覆盖央视全频道和各大卫视&#xff0c;各地地方台也能一网打尽&#xff0c;随时随地看高清电视。各卫视台覆盖广泛&#xff0c;包括浙江电视台、湖南卫视、江苏卫视、东方卫视等全部卫视台&#xff0c;最新内容先一步掌握。拥有广东、北京、风云足球等热播体育频…

书生大模型实战营第四期-入门岛第一关

关卡任务 好的&#xff0c;我们废话不多说&#xff0c;开始闯关Go,Go,Go! 闯关任务 SSH连接到本地使用开发机是很舒服的&#xff0c;但是我们该如何连接呢&#xff0c;别急&#xff0c;书生给我们教程了 这里我们展示结果&#xff0c;对了&#xff0c;本地SSH连接需要安装一些…

臻于智境 安全护航 亚信安全受邀出席新华三智算新品发布会

近日&#xff0c;紫光股份旗下新华三集团在北京隆重举办了主题为“乘势 进化 臻于智境”的新华三智算新品发布会。作为新华三集团的长期战略合作伙伴&#xff0c;亚信安全受邀参会&#xff0c;亚信安全CEO马红军出席发布仪式&#xff0c;并与来自各界的业界伙伴共同探讨智能化…

错题收集app有哪些?5个软件帮助你快速进行app收集

错题收集app有哪些&#xff1f;5个软件帮助你快速进行app收集 以下是五款便捷实用的错题收集 App&#xff0c;能够帮助你更高效地记录、整理和复习错题&#xff1a; 试卷全能宝 试卷全能宝 是一款多功能试卷识别和编辑软件&#xff0c;适合用于错题整理和记录。你可以创建错…