libVLC 音频输出设备切换

libvlc_audio_output_list_get和libvlc_audio_output_device_list_get是libVLC 库中用于处理音频输出的两个函数。

  • libvlc_audio_output_list_get函数用于获取可用的音频输出模块列表。这个列表通常包括不同的音频输出方式,例如 Pulseaudio、ALSA 等。通过这个函数,可以了解有哪些音频输出模块可供使用。
  • libvlc_audio_output_device_list_get函数则是用于获取特定音频输出模块下的音频输出设备列表。这个函数通常在选择了特定的音频输出模块后使用,用于列出该模块下的具体设备,例如特定的声卡或硬件设备。

libvlc_audio_output_list_get用于获取音频输出模块的列表。

libvlc_audio_output_device_list_get用于获取特定模块下的音频输出设备的列表。

在实际应用中,需要先使用 libvlc_audio_output_list_get获取音频输出模块,然后针对每个模块使用 libvlc_audio_output_device_list_get获取其下的设备列表,以便于用户选择和配置。

以下是VLC播放器中使用的截图界面,我们仿照这个做一个界面。 

以下是效果图。

首先程序运行起来获取音频设备列表,创建菜单项。

    QList<QString> m_lstAudioDevice;	m_lstAudioDevice.clear();libvlc_audio_output_device_t *pDevList = nullptr;libvlc_audio_output_t *pOutputList = libvlc_audio_output_list_get(vlc_base);while (pOutputList){//获取设备pDevList = libvlc_audio_output_device_list_get(vlc_base, pOutputList->psz_name);while (pDevList){// 找到我想要的那个设备,跳出循环,这里使用i控制,我知道我想要的设备位置if (strcmp(pOutputList->psz_name, "mmdevice") == 0){QString des = QString::fromStdString(std::string(pDevList->psz_description, strlen(pDevList->psz_description)));m_lstAudioDevice.append(des);QAction *action = m_deviceMenu->addAction(des);action->setData(pDevList->psz_device);m_group->addAction(action);}pDevList = pDevList->p_next;}pOutputList = pOutputList->p_next;}

libvlc_audio_output_list_get获取音频输出模块的列表。

libvlc_audio_output_device_list_get获取特定模块下的音频输出设备的列表。

这里我找到名为"mmdevice"模块下的所有音频输出设备,存储到一个QList中。

setData设置设备标识符字符串。方便我们在切换的时候使用这个标识符。

typedef struct libvlc_audio_output_device_t
{struct libvlc_audio_output_device_t *p_next; /**< Next entry in list */char *psz_device; /**< Device identifier string */char *psz_description; /**< User-friendly device description *//* More fields may be added here in later versions */
} libvlc_audio_output_device_t;

因为在libvlc_audio_output_device_set这个接口中,参数device_id就是上面的设备标识符。

LIBVLC_API void libvlc_audio_output_device_set( libvlc_media_player_t *mp,const char *module,const char *device_id );

如果需要切换音频输出模块,调用libvlc_audio_output_set。

/*** Selects an audio output module.* \note Any change will take be effect only after playback is stopped and* restarted. Audio output cannot be changed while playing.** \param p_mi media player* \param psz_name name of audio output,*               use psz_name of \see libvlc_audio_output_t* \return 0 if function succeeded, -1 on error*/
LIBVLC_API int libvlc_audio_output_set( libvlc_media_player_t *p_mi,const char *psz_name );

官方的解释是,你需要先停止播放,设置新的音频输出设备,然后重新开始播放,以便新的设置生效。 

示例代码:

	libvlc_media_player_stop(vlc_mediaPlayer);//设置输出waveoutint ret = libvlc_audio_output_set(vlc_mediaPlayer, "mmdevice");//指定设备输出pDevList->psz_devicelibvlc_audio_output_device_set(vlc_mediaPlayer, NULL, txt.toStdString().c_str());libvlc_media_player_play(vlc_mediaPlayer);

如果需要切换音频输出设备,设备在同一模块下,就不需要调用libvlc_audio_output_set,直接调用libvlc_audio_output_device_set,这个是不需要停止播放,再重新播放。

 * \param mp media player* \param module If NULL, current audio output module.*               if non-NULL, name of audio output module(\see libvlc_audio_output_t)* \param device_id device identifier string* \return Nothing. Errors are ignored (this is a design bug).*/
LIBVLC_API void libvlc_audio_output_device_set( libvlc_media_player_t *mp,const char *module,const char *device_id );

官方解释,参数module插入参数NULL,就使用当前的音频输出模块。 

示例代码:

void showWidget::slotActionTriggered(QAction *action)
{if (!vlc_mediaPlayer)return;QString txt = action->data().toString();//指定设备输出pDevList->psz_devicelibvlc_audio_output_device_set(vlc_mediaPlayer, NULL, txt.toStdString().c_str());
}

完整源码:本示例演示同一模块下,输出设备的切换。

#pragma once#include <QtWidgets/QWidget>
#include "ui_showWidget.h"
#include <QMenu>
#include <QActionGroup>
#include <vlc/vlc.h>
#include <QDebug>
#include <QFileDialog>
#include <QThread>
#include <QMouseEvent>
#include <QKeyEvent>enum Rate
{Rate2X,Rate1_5X,Rate1_25X,Rate1_0X,Rate0_75X,Rate0_5X
};class showWidget : public QWidget
{Q_OBJECTpublic:showWidget(QWidget *parent = nullptr);~showWidget();private slots:void slotOpenFile();void slotPlay();void slotPause();void slotStop();void slotValueChanged(int value);void slotCurrentIndexChanged(int index);void slotActionTriggered(QAction *action);protected:virtual void mousePressEvent(QMouseEvent *event);private://事件处理回调static void vlcEvents(const libvlc_event_t *ev, void *param);private:Ui::showWidgetClass ui;private:libvlc_instance_t *vlc_base = nullptr;libvlc_media_t *vlc_media = nullptr;libvlc_media_player_t *vlc_mediaPlayer = nullptr;QList<float> m_lstRate;QList<QString> m_lstAudioDevice;QMenu *m_menu = nullptr;QMenu *m_deviceMenu = nullptr;QAction *m_actionAudioDev = nullptr;	//音频设备QActionGroup *m_group = nullptr;
};

cpp文件。 

#include "showWidget.h"
#include <QTimer>
#include <QTime>#pragma execution_character_set("utf-8")showWidget::showWidget(QWidget *parent): QWidget(parent)
{ui.setupUi(this);this->setWindowTitle("视频播放器");m_menu = new QMenu(this);m_actionAudioDev = m_menu->addAction("音频设备");m_deviceMenu = new QMenu(this);m_actionAudioDev->setMenu(m_deviceMenu);m_group = new QActionGroup(this);connect(m_group, &QActionGroup::triggered, this, &showWidget::slotActionTriggered);vlc_base = libvlc_new(0, NULL);m_lstAudioDevice.clear();libvlc_audio_output_device_t *pDevList = nullptr;libvlc_audio_output_t *pOutputList = libvlc_audio_output_list_get(vlc_base);while (pOutputList){//获取设备pDevList = libvlc_audio_output_device_list_get(vlc_base, pOutputList->psz_name);while (pDevList){// 找到我想要的那个设备,跳出循环,这里使用i控制,我知道我想要的设备位置if (strcmp(pOutputList->psz_name, "mmdevice") == 0){QString des = QString::fromStdString(std::string(pDevList->psz_description, strlen(pDevList->psz_description)));m_lstAudioDevice.append(des);QAction *action = m_deviceMenu->addAction(des);action->setData(pDevList->psz_device);m_group->addAction(action);}pDevList = pDevList->p_next;}pOutputList = pOutputList->p_next;}ui.cbxRate->setCurrentIndex(Rate1_0X);m_lstRate << 2.0 << 1.5 << 1.25 << 1.0 << 0.75 << 0.5;ui.btnOpen->setFocusPolicy(Qt::NoFocus);ui.btnPlay->setFocusPolicy(Qt::NoFocus);ui.btnPause->setFocusPolicy(Qt::NoFocus);ui.btnStop->setFocusPolicy(Qt::NoFocus);ui.hSliderVolumn->setFocusPolicy(Qt::NoFocus);ui.cbxRate->setFocusPolicy(Qt::NoFocus);connect(ui.btnOpen, &QPushButton::clicked, this, &showWidget::slotOpenFile);connect(ui.btnPlay, &QPushButton::clicked, this, &showWidget::slotPlay);connect(ui.btnPause, &QPushButton::clicked, this, &showWidget::slotPause);connect(ui.btnStop, &QPushButton::clicked, this, &showWidget::slotStop);connect(ui.hSliderVolumn, &QSlider::valueChanged, this, &showWidget::slotValueChanged);connect(ui.cbxRate,SIGNAL(currentIndexChanged(int)), this, SLOT(slotCurrentIndexChanged(int)));
}showWidget::~showWidget()
{libvlc_release(vlc_base); //减少libvlc实例的引用计数,并销毁
}void showWidget::slotOpenFile()
{/*选择文件*/QString filename = QFileDialog::getOpenFileName(this, "选择打开的文件", "D:/", tr("*.*"));std::replace(filename.begin(), filename.end(), QChar('/'), QChar('\\'));vlc_media = libvlc_media_new_path(vlc_base, filename.toUtf8().data());if (!vlc_media) {return;}// 创建libvlc实例和媒体播放器vlc_mediaPlayer = libvlc_media_player_new_from_media(vlc_media);if (!vlc_mediaPlayer) {return;}//libvlc_media_add_option(vlc_media, "--video-filter=transform");//libvlc_media_add_option(vlc_media, "---transform-type=270");// 等待元数据加载完成libvlc_media_parse(vlc_media);libvlc_video_set_mouse_input(vlc_mediaPlayer, 0);libvlc_video_set_key_input(vlc_mediaPlayer, 0);// 获取各种元数据const char *title = libvlc_media_get_meta(vlc_media, libvlc_meta_Title);const char *artist = libvlc_media_get_meta(vlc_media, libvlc_meta_Artist);const char *album = libvlc_media_get_meta(vlc_media, libvlc_meta_Album);const char *url = libvlc_media_get_meta(vlc_media, libvlc_meta_URL);const char *date = libvlc_media_get_meta(vlc_media, libvlc_meta_Date);const char *lang = libvlc_media_get_meta(vlc_media, libvlc_meta_Language);int duration = libvlc_media_get_duration(vlc_media);  // 获取时长(单位:毫秒)qDebug("Title: %s", title ? title : "N/A");qDebug("Artist: %s", artist ? artist : "N/A");qDebug("Album: %s", album ? album : "N/A");qDebug("Duration: %d ms", duration);qDebug("url: %s", url ? url : "N/A");qDebug("date: %s", date ? date : "N/A");qDebug("lang: %s", lang ? lang : "N/A");libvlc_media_track_t **tracks;int track_count = libvlc_media_tracks_get(vlc_media,&tracks);for (unsigned i = 0; i < track_count; i++) {libvlc_media_track_t* track = tracks[i];// 显示轨道信息printf("Track #%u: %s\n", i, track->psz_description);// 这里可以获取到每一个轨道的信息,比如轨道类型 track->i_type// 可能是 libvlc_track_video, libvlc_track_audio 或者 libvlc_track_text (字幕)if (track->i_type == libvlc_track_video) {// 处理视频轨道信息qDebug("width = %d",track->video->i_width);qDebug("height = %d", track->video->i_height);qDebug("rate_num = %d", track->video->i_frame_rate_num);qDebug("rate_den = %d", track->video->i_frame_rate_den);}else if (track->i_type == libvlc_track_audio) {// 处理音频轨道信息qDebug("channels = %d", track->audio->i_channels);qDebug("rate = %d", track->audio->i_rate);}else if (track->i_type == libvlc_track_text) {// 处理字幕轨道信息}}//获取事件管理器libvlc_event_manager_t *em = libvlc_media_player_event_manager(vlc_mediaPlayer);// 注册事件监听器libvlc_event_attach(em, libvlc_MediaPlayerTimeChanged, vlcEvents, this);libvlc_event_attach(em, libvlc_MediaPlayerEndReached, vlcEvents, this);libvlc_event_attach(em, libvlc_MediaPlayerStopped, vlcEvents, this);libvlc_event_attach(em, libvlc_MediaPlayerPlaying, vlcEvents, this);libvlc_event_attach(em, libvlc_MediaPlayerPaused, vlcEvents, this);libvlc_media_player_set_hwnd(vlc_mediaPlayer, (void *)ui.widgetShow->winId());libvlc_video_set_adjust_int(vlc_mediaPlayer, libvlc_adjust_Enable, 1);QTimer::singleShot(1000, this, &showWidget::slotPlay);libvlc_video_filter_list_get(vlc_base);}void showWidget::slotPlay()
{if (vlc_mediaPlayer){libvlc_media_player_play(vlc_mediaPlayer);}}void showWidget::slotPause()
{if (vlc_mediaPlayer)libvlc_media_player_pause(vlc_mediaPlayer);
}void showWidget::slotStop()
{if (vlc_mediaPlayer)libvlc_media_player_stop(vlc_mediaPlayer);
}void showWidget::slotValueChanged(int value)
{if (vlc_mediaPlayer)libvlc_audio_set_volume(vlc_mediaPlayer, value);
}void showWidget::slotCurrentIndexChanged(int index)
{if (vlc_mediaPlayer)libvlc_media_player_set_rate(vlc_mediaPlayer, m_lstRate[index]);
}void showWidget::slotActionTriggered(QAction *action)
{if (!vlc_mediaPlayer)return;QString txt = action->data().toString();//指定设备输出pDevList->psz_devicelibvlc_audio_output_device_set(vlc_mediaPlayer, NULL, txt.toStdString().c_str());
}void showWidget::mousePressEvent(QMouseEvent *event)
{switch (event->button()){case Qt::RightButton://this->setWindowState(Qt::WindowMinimized);m_menu->exec(event->globalPos());break;default:QWidget::mousePressEvent(event);}
}//事件回调
void showWidget::vlcEvents(const libvlc_event_t *ev, void *param)
{showWidget *w = (showWidget*)param;//处理不同的事件switch (ev->type) {case libvlc_MediaPlayerTimeChanged:{//qDebug() << "VLC媒体播放器时间已更改";qint64 len = libvlc_media_player_get_time(w->vlc_mediaPlayer);libvlc_time_t lenSec = len / 1000;libvlc_time_t totalLen = libvlc_media_player_get_length(w->vlc_mediaPlayer);libvlc_time_t totalLenSec = totalLen / 1000;int thh, tmm, tss;thh = lenSec / 3600;tmm = (lenSec % 3600) / 60;tss = (lenSec % 60);QTime time(thh, tmm, tss);w->ui.lbCurTime->setText(time.toString("hh:mm:ss"));thh = totalLenSec / 3600;tmm = (totalLenSec % 3600) / 60;tss = (totalLenSec % 60);QTime TotalTime(thh, tmm, tss);w->ui.lbTotalTime->setText(TotalTime.toString("hh:mm:ss"));double pos = (double)lenSec / totalLenSec * 100;w->ui.horizontalSlider->setValue(pos);}break;case libvlc_MediaPlayerEndReached:qDebug() << "VLC播放完毕.";break;case libvlc_MediaPlayerStopped:qDebug() << "VLC停止播放";break;case libvlc_MediaPlayerPlaying:qDebug() << "VLC开始播放";break;case libvlc_MediaPlayerPaused:qDebug() << "VLC暂停播放";break;}
}

更多参考:

libVLC 事件机制-CSDN博客

libVLC windows开发环境搭建-CSDN博客

libVLC 视频抓图_libvlc 截图-CSDN博客

libVLC 动态视频壁纸-CSDN博客

libVLC 设置滤镜-CSDN博客

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

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

相关文章

Linux——用户管理,文件压缩命令

用户管理命令 (1)系统存储用户信息的位置: /etc/passwd:存储用户的基本信息 UID:用户ID GID:组ID; (2)系统存储组信息的位置: /etc/group (3)系统存储用户密码信息的位置: /etc/shadow (2)添加用户 使用命令添加新用户:useradd newname 桌面添加:右键:设置:用户,解锁,添加用…

算法第三十九天-验证二叉树的前序序列化

验证二叉树的前序序列化 题目要求 解题思路 方法一&#xff1a;栈 栈的思路是「自底向上」的想法。下面要结合本题是「前序遍历」这个重要特点。 我们知道「前序遍历」是按照「根节点-左子树-右子树」的顺序遍历的&#xff0c;只有当根节点的所有左子树遍历完成之后&#xf…

排查Java中的OOM(Out of Memory)问题

Java的OOM&#xff08;OutOfMemoryError&#xff09;问题通常表示Java虚拟机&#xff08;JVM&#xff09;在尝试分配内存给对象时&#xff0c;无法找到足够的连续内存空间。这可能是由于内存泄漏、堆内存不足或其他原因导致的。排查OOM问题通常涉及以下几个步骤&#xff1a; 查…

使用 Docker 部署 Photopea 在线 PS 工具

1&#xff09;Photopea 介绍 GitHub&#xff1a;https://github.com/photopea/photopea 官方手册&#xff1a;https://www.photopea.com/learn/ Adobe 出品的「PhotoShop」想必大家都很熟悉啦&#xff0c;但是「PhotoShop」现在对电脑配置要求越来越高&#xff0c;体积越来越大…

逆向入门:为ctf国赛而写的笔记 day01

目录 通用寄存器&#xff1a; EAX:累加寄存器&#xff0c;是很多加法乘法指令的缺省寄存器 EBX&#xff1a;基地址寄存器&#xff0c;在内存寻址时存放基地址 ECX&#xff1a;计数器 EDX&#xff1a;数据寄存器&#xff0c;被用于来放整数除法产生的余数 变址寄存器 标志…

流行的API架构学习

几种流行的API架构风格图 SOAP&#xff08;Simple Object Access Protocol&#xff09; 优点&#xff1a;SOAP 是一种基于 XML 的通信协议&#xff0c;具有良好的跨平台和跨语言支持。它提供了丰富的安全性和事务管理功能&#xff0c;并支持复杂的消息交换模式。 缺点&#xf…

windows,web端网页唤起打开本地的客户端程序

这里写自定义目录标题 需求&#xff1a;在电脑浏览器网页唤起本地的应用程序 使用类似以下代码 <a href"myprotocol:">打开飞书</a>在客户端安装的时候在注册表会有自己的协议&#xff0c;若是没有的可自定义注册表 自定义注册表步骤 1.winr 运行 regedi…

物联网工程-系统设计作业

1.设计一套基于RFID牛场养殖信息管理系统&#xff0c;并给出系统设计思路、系统构架和控制流程图。 一、设计思想 为方便牛场养殖员鉴别和管理牛群&#xff0c;为每只牛佩戴有RFID标签的动物耳钉&#xff0c;并将牛的健康情况录入数据库中&#xff0c;随着牛的生长&#xff0c;…

关于递归和回溯的思考

完整代码: 力扣112路径总和 class Solution { private:bool traversal(TreeNode* cur, int count) {if (!cur->left && !cur->right && count 0) return true; // 遇到叶子节点&#xff0c;并且计数为0if (!cur->left && !cur->right) r…

[StartingPoint][Tier1]Funnel

Task 1 How many TCP ports are open? (打开了多少个 TCP 端口&#xff1f;) # nmap -sS -T4 10.129.224.226 --min-rate 1000 2 Task 2 What is the name of the directory that is available on the FTP server? (FTP 服务器上可用的目录名称是什么&#xff1f;) $ n…

数据库系统概论(超详解!!!)第三节 关系数据库标准语言SQL(Ⅵ)

1.空值的处理 空值就是“不知道”或“不存在”或“无意义”的值。 一般有以下几种情况&#xff1a; 该属性应该有一个值&#xff0c;但目前不知道它的具体值 &#xff1b;该属性不应该有值 &#xff1b;由于某种原因不便于填写。 1.空值的产生 空值是一个很特殊的值&#x…

云仓酒庄旗下雷盛红酒入驻香港星怡SingLa餐厅共绘美食美酒新篇章

近日&#xff0c;云仓酒庄旗下品牌雷盛红酒正式入驻香港餐厅星怡SingLa&#xff0c;这一跨界合作不仅为香港市民和游客带来了全新的味蕾享受&#xff0c;也标志着美食与美酒文化的很好结合&#xff0c;共同绘就了一幅精彩绝伦的美食美酒新篇章。 云仓酒庄一直以来都致力于为消费…

Rust 程序设计语言学习——枚举模式匹配

枚举&#xff08;enumerations&#xff09;&#xff0c;也被称作 enums。match 允许我们将一个值与一系列的模式相比较&#xff0c;并根据相匹配的模式执行相应代码。 1 枚举的定义 假设我们要跨省出行&#xff0c;有多种交通工具供选择。常用的交通工具有飞机、火车、汽车和轮…

备战蓝桥杯Day37 - 真题 - 特殊日期

一、题目描述 思路&#xff1a; 1、统计2000年到2000000年的日期&#xff0c;肯定是需要遍历 2、闰年的2月是29天&#xff0c;非闰年的2月是28天。我们需要判断这一年是否是闰年。 1、3、5、7、8、10、12月是31天&#xff0c;4、6、9、11月是30天。 3、年份yy是月份mm的倍数…

【Entity Framework】EF配置文件设置详解

【Entity Framework】EF配置文件设置详解 文章目录 【Entity Framework】EF配置文件设置详解一、概述二、实体框架配置部分三、连接字符串四、EF数据库提供程序五、EF侦听器六、将数据库操作记录到文件中七、Code First默认连接工厂八、数据库初始值设定项 一、概述 EF实体框架…

OKR应用层级与试点部门选择:管理层与员工层的应用探讨

OKR&#xff08;Objectives and Key Results&#xff09;作为一种高效的目标管理工具&#xff0c;其应用层级的选择对于企业的实施效果至关重要。在管理层和员工层之间&#xff0c;并没有绝对的先后顺序&#xff0c;而是需要根据企业的具体情况和需求进行灵活应用。同时&#x…

CODEFORCES --- 630A. Again Twenty Five!

630A. Again Twenty Five! 人力资源经理又失望了。最后一名应聘者和之前的 24 名应聘者一样&#xff0c;都没有通过面试。"我应该给这样一个艰巨的任务吗&#xff1f;- 人力资源经理想。“只要把数字 5 提高到 n 的幂&#xff0c;然后得到数字的最后两位就可以了。是的&a…

stata 数据匹配

横向匹配&#xff08;增加变量&#xff09;——merge merge 1:1 id using otherfile.dta匹配城市 merge m:1 city using "E:\基点.dta",nogen匹配上市公司 merge m:1 stkcd time using "E:\基点.dta",nogen匹配类型&#xff1a; 1:1: 1配1 m:1:多配1 …

QEMU介绍

原文位置&#xff1a;https://github.com/qemu/qemu 原文 QEMU is a generic and open source machine & userspace emulator and virtualizer. QEMU is capable of emulating a complete machine in software without any need for hardware virtualization support. B…

前端八股文面试题——webpack工程化

前端工程化面试题 webpack有哪些常见的loader? 你用过哪些loader?webpack 有哪些常见的Plugin? 你用过哪些Plugin?说说Loader 和Plugin 的区别作用上结构上 webpack 构建流程简单说一下使用webpack开发时&#xff0c;使用过哪些可以提高效率的插件&#xff1f;如何优化webp…