FFmpeg+javacpp中纯音频播放

  • 1. Java Sound播放
  • 2、整合音频信息AudioInfo
  • 3、添加ExecutorService执行播放

FFmpeg+javacpp+javacv使用
FFmpeg+javacpp中FFmpegFrameGrabber
FFmpeg+javacpp中仿ffplay播放

JavaCV 1.5.12 API
JavaCPP Presets for FFmpeg 7.1.1-1.5.12 API

1. Java Sound播放

如FFmpeg+javacpp中仿ffplay播放中 2.1 grabSamples() 播放案例
gitee: xhbruce/JavacvAddFFmpegDemo

public class AudioPlayer implements Runnable {private final String filePath;public AudioPlayer(String filePath) {this.filePath = filePath;//AvLog.close();}public void playAudio() throws Exception {FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(filePath);grabber.start();int audioBitrate = grabber.getAudioBitrate(); // 获取音频流的比特率(bitrate)信息int audioChannels = grabber.getAudioChannels(); // 获取当前音频流的通道数量(声道数)int audioCodec = grabber.getAudioCodec(); //String audioCodecName = grabber.getAudioCodecName(); // 获取当前音频流所使用的音频编码器(Codec)名称double audioFrameRate = grabber.getAudioFrameRate(); //Map<String, String> audioMetadata = grabber.getAudioMetadata(); //Map<String, Buffer> audioSideData = grabber.getAudioSideData(); //int lengthInAudioFrames = grabber.getLengthInAudioFrames(); // 获取音频流的总帧数(以音频帧为单位)boolean hasAudio = grabber.hasAudio(); // 是否有音频流int sampleFormat = grabber.getSampleFormat(); // 获取音频采样格式(Sample Format)int sampleRate = grabber.getSampleRate(); // 获取音频流的采样率(Sample Rate)XLog.i("audioBitrate: " + audioBitrate + ", audioChannels: " + audioChannels + ", audioCodec: " + audioCodec + ", audioCodecName: " + audioCodecName+ ", audioFrameRate: " + audioFrameRate + ", audioMetadata=" + audioMetadata + ", audioSideData=" + audioSideData + ", lengthInAudioFrames: " + lengthInAudioFrames+ ", hasAudio=" + hasAudio + ", sampleFormat: " + sampleFormat + ",sampleRate: " + sampleRate);AudioFormat audioFormat = AudioUtil.toAudioFormat(sampleFormat, sampleRate, audioChannels);XLog.d("audioFormat: " + audioFormat);DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);SourceDataLine audioLine = (SourceDataLine) AudioSystem.getLine(info);audioLine.open(audioFormat);audioLine.start();Frame frame;long totalDurationMs = grabber.getLengthInTime(); // 获取总时长(毫秒)XLog.d("音频总时长: " + totalDurationMs + " ms -- " + AudioUtil.getDurationString(totalDurationMs));while ((frame = grabber.grabSamples()) != null) {if (frame.samples == null || frame.samples.length == 0) continue;Buffer buffer = frame.samples[0];byte[] audioBytes = AudioUtil.toByteArrayApplySourceDataLine(buffer);audioLine.write(audioBytes, 0, audioBytes.length);}audioLine.drain();audioLine.stop();audioLine.close();grabber.stop();}@Overridepublic void run() {try {playAudio();} catch (Exception e) {throw new RuntimeException(e);}}public static void main(String[] args) {String musicUrl = "F:\\Music\\Let Me Down Slowly.mp3";
//        String musicUrl = "F:\\Music\\张碧晨 - 凉凉.flac";
//        String musicUrl = "F:\\Music\\爱得比你深 - 张学友.ape";new Thread(new AudioPlayer(musicUrl)).start();}
}

2、整合音频信息AudioInfo

audioBitrate = grabber.getAudioBitrate(); // 获取音频流的比特率(bitrate)信息
audioChannels = grabber.getAudioChannels(); // 获取当前音频流的通道数量(声道数)
audioCodec = grabber.getAudioCodec(); //
audioCodecName = grabber.getAudioCodecName(); // 获取当前音频流所使用的音频编码器(Codec)名称
audioFrameRate = grabber.getAudioFrameRate(); //
metadata = new Metadata(grabber.getMetadata()); //
audioMetadata = grabber.getAudioMetadata(); //
audioSideData = grabber.getAudioSideData(); //
lengthInAudioFrames = grabber.getLengthInAudioFrames(); // 获取音频流的总帧数(以音频帧为单位)
sampleFormat = grabber.getSampleFormat(); // 获取音频采样格式(Sample Format)
sampleRate = grabber.getSampleRate(); // 获取音频流的采样率(Sample Rate)
ext = grabber.getFormat(); //grabber.getFormatContext().iformat().name().getString();
totalDurationMs = grabber.getLengthInTime(); // 获取总时长(毫秒)

https://gitee.com/xhbruce/JavacvAddFFmpegDemo/blob/master/src/main/java/org/xhbruce/ffmpeg/info/AudioInfo.java

import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.xhbruce.utils.AudioUtil;import java.nio.Buffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;/*** @author xhbruce*/
public class AudioInfo {private static final String TAG = "AudioInfo";public FFmpegFrameGrabber grabber;public Path filePath;public String ext; //文件格式public int audioBitrate;public int audioChannels;public int audioCodec;public String audioCodecName;public double audioFrameRate;Metadata metadata;// tag信息Map<String, String> audioMetadata;Map<String, Buffer> audioSideData;public int lengthInAudioFrames;public int sampleFormat;public int sampleRate;public long totalDurationMs;public long startTime;public AudioInfo(String url) {grabber = new FFmpegFrameGrabber(url);filePath = Paths.get(url);try {init();} catch (FFmpegFrameGrabber.Exception e) {throw new RuntimeException(e);}}private void init() throws FFmpegFrameGrabber.Exception {grabber.start();if (grabber.hasAudio()/*是否有音频流*/) {audioBitrate = grabber.getAudioBitrate(); // 获取音频流的比特率(bitrate)信息audioChannels = grabber.getAudioChannels(); // 获取当前音频流的通道数量(声道数)audioCodec = grabber.getAudioCodec(); //audioCodecName = grabber.getAudioCodecName(); // 获取当前音频流所使用的音频编码器(Codec)名称audioFrameRate = grabber.getAudioFrameRate(); //metadata = new Metadata(grabber.getMetadata()); //audioMetadata = grabber.getAudioMetadata(); //audioSideData = grabber.getAudioSideData(); //lengthInAudioFrames = grabber.getLengthInAudioFrames(); // 获取音频流的总帧数(以音频帧为单位)sampleFormat = grabber.getSampleFormat(); // 获取音频采样格式(Sample Format)sampleRate = grabber.getSampleRate(); // 获取音频流的采样率(Sample Rate)ext = grabber.getFormat(); //grabber.getFormatContext().iformat().name().getString();totalDurationMs = grabber.getLengthInTime(); // 获取总时长(毫秒)startTime = grabber.getFormatContext().start_time();}grabber.stop();}public String getMetadata(String key) {return metadata.getValue(key);}@Overridepublic String toString() {StringBuilder stringBuffer = new StringBuilder("AudioInfo [\nInput #0, " + ext + "(" + audioCodecName + "), from '" + filePath.toAbsolutePath() + "': title: " + getMetadata(Metadata.TITLE) + ", artist: " + getMetadata(Metadata.ARTIST) + "\n");stringBuffer.append(metadata.toString());stringBuffer.append("Duration: ").append(AudioUtil.getDurationString(totalDurationMs)).append(", start: ").append(AudioUtil.getStartTimeString(startTime)).append(", sampleRate: ").append(sampleRate).append(" HZ").append(", bitrate: ").append(audioBitrate / 1000).append(" kb/s\n");if (!audioMetadata.isEmpty()) stringBuffer.append("AudioMetadata: ").append(audioMetadata.size()).append("\n");for (Map.Entry<String, String> entry : audioMetadata.entrySet()) {stringBuffer.append(String.format("%-14s%s", "\t" + entry.getKey(), ":\t" + entry.getValue())).append("\n");}if (!audioSideData.isEmpty()) stringBuffer.append("AudioSideData: ").append(audioSideData.size()).append("\n");for (Map.Entry<String, Buffer> entry : audioSideData.entrySet()) {stringBuffer.append(String.format("%-14s%s", "\t" + entry.getKey(), ":\t" + entry.getValue())).append("\n");}stringBuffer.append("]");return stringBuffer.toString();}
}
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.ffmpeg.avutil.AVDictionary;
import org.bytedeco.ffmpeg.avutil.AVDictionaryEntry;import java.util.HashMap;
import java.util.Map;import static org.bytedeco.ffmpeg.global.avutil.*;/*** @author xhbruce** ffmpeg-6.0\libavformat\avformat.h* album        -- name of the set this work belongs to* album_artist -- main creator of the set/album, if different from artist.* e.g. "Various Artists" for compilation albums.* artist       -- main creator of the work* comment      -- any additional description of the file.* composer     -- who composed the work, if different from artist.* copyright    -- name of copyright holder.* creation_time-- date when the file was created, preferably in ISO 8601.* date         -- date when the work was created, preferably in ISO 8601.* disc         -- number of a subset, e.g. disc in a multi-disc collection.* encoder      -- name/settings of the software/hardware that produced the file.* encoded_by   -- person/group who created the file.* filename     -- original name of the file.* genre        -- <self-evident>.* language     -- main language in which the work is performed, preferably* in ISO 639-2 format. Multiple languages can be specified by* separating them with commas.* performer    -- artist who performed the work, if different from artist.* E.g for "Also sprach Zarathustra", artist would be "Richard* Strauss" and performer "London Philharmonic Orchestra".* publisher    -- name of the label/publisher.* service_name     -- name of the service in broadcasting (channel name).* service_provider -- name of the service provider in broadcasting.* title        -- name of the work.* track        -- number of this work in the set, can be in form current/total.* variant_bitrate -- the total bitrate of the bitrate variant that the current stream is part of*/
public class Metadata {private AVDictionary avDictionary;private Map<String, String> metadata = new HashMap<String, String>();public static final String ALBUM = "album";public static final String ALBUM_ARTIST = "album_artist";public static final String ARTIST = "artist";public static final String COMMENT = "comment";public static final String COMPOSER = "composer";public static final String COMPYRIGHT = "copyright";public static final String CREATION_TIME = "creation_time";public static final String DATE = "date";public static final String DISC = "disc";public static final String ENCODER = "encoder";public static final String ENCODER_BY = "encoded_by";public static final String FILENAME = "filename";public static final String GENRE = "genre";public static final String LANGUAGE = "language";public static final String PERFORMER = "performer";public static final String PUBLISHER = "publisher";public static final String SERVICE_NAME = "service_name";public static final String SERVICE_PROVIDER = "service_provider";public static final String TITLE = "title";public static final String TRACK = "track";public static final String VARIANT_BITRATE = "variant_bitrate";private int dictCount;public Metadata(AVFormatContext formatContext) {this(formatContext.metadata());}public Metadata(AVDictionary dictionary) {avDictionary = dictionary;dictCount = av_dict_count(avDictionary);initMetadate();}private void initMetadate() {metadata = new HashMap<String, String>();AVDictionaryEntry tag = null;while ((tag = av_dict_iterate(avDictionary, tag)) != null) {String key = tag.key().getString();String value = tag.value().getString();metadata.put(key, value);}}public Metadata(Map<String, String> metadata) {this.metadata = metadata;dictCount = metadata.size();}/*** or av_dict_get(avDictionary, key, null, 0);*/public String getValue(String key) {return metadata.get(key);}public int size() {return dictCount;}@Overridepublic String toString() {StringBuilder stringBuffer = new StringBuilder();stringBuffer.append("Metadata: ").append(dictCount).append("\n");for (Map.Entry<String, String> entry : metadata.entrySet()) {stringBuffer.append(String.format("%-14s%s", "\t" + entry.getKey(), ":\t" + entry.getValue())).append("\n");}return stringBuffer.toString();}
}

3、添加ExecutorService执行播放

implementation 'com.github.goxr3plus:java-stream-player:+' 实质也是Java Sound播放,作为参考。

  • ExecutorService audioPlayerExecutorServicec护理音频播放
  • ExecutorService eventsExecutorService处理音频播放状态
public class AudioPlayer implements AudioPlayerInterface, Callable<Void> {private AudioInfo audioInfo;private volatile AudioInputStream audioInputStream;private AudioFormat audioFormat;private DataLine.Info info;private SourceDataLine audioLine;private final Object audioLock = new Object();/*** audio 播放服务*/private final ExecutorService audioPlayerExecutorService;/*** audio 播放状态更新服务*/private final ExecutorService eventsExecutorService;/*** 通知监听事件*/private final ArrayList<AudioPlayerListener> listeners;public AudioPlayer() {audioPlayerExecutorService = Executors.newSingleThreadExecutor();eventsExecutorService = Executors.newSingleThreadExecutor();listeners = new ArrayList<>();}@Overridepublic void addStreamPlayerListener(AudioPlayerListener audioPlayerListener) {listeners.add(audioPlayerListener);}@Overridepublic void removeStreamPlayerListener(AudioPlayerListener audioPlayerListener) {if (listeners != null) {listeners.remove(audioPlayerListener);}}public void open(String filePath) {open(new File(filePath));}@Overridepublic void open(File file) {audioInfo = new AudioInfo(file);initAudioInputStream();try {audioInfo.grabber.start();} catch (FFmpegFrameGrabber.Exception e) {throw new RuntimeException(e);}}private void initAudioInputStream() {audioFormat = AudioUtil.toAudioFormat(audioInfo.sampleFormat, audioInfo.sampleRate, audioInfo.audioChannels);createAudioLine();}private void createAudioLine() {try {info = new DataLine.Info(SourceDataLine.class, audioFormat);audioLine = (SourceDataLine) AudioSystem.getLine(info);XLog.i("Line : " + audioLine);XLog.i("Line Info : " + info);XLog.i("Line AudioFormat: " + audioFormat);audioLine.open(audioFormat);} catch (LineUnavailableException e) {throw new RuntimeException(e);}}@Overridepublic void play() {audioLine.start();audioPlayerExecutorService.submit(this);}@Overridepublic boolean pause() {return false;}@Overridepublic boolean resume() {return false;}@Overridepublic void stop() {}@Overridepublic Void call() throws FFmpegFrameGrabber.Exception {XLog.d("响应 audioPlayerExecutorService");synchronized (audioLock) {Frame frame;while ((frame = audioInfo.grabber.grabSamples()) != null) {if (frame.samples == null || frame.samples.length == 0) continue;Buffer buffer = frame.samples[0];byte[] audioBytes = AudioUtil.toByteArrayApplySourceDataLine(buffer);audioLine.write(audioBytes, 0, audioBytes.length);}}audioLine.drain();audioLine.stop();audioLine.close();audioInfo.grabber.stop();return null;}
}

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

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

相关文章

洛谷P1036 [NOIP 2002 普及组] 选数

P1036 [NOIP 2002 普及组] 选数 题目描述 已知 nnn 个整数 x1,x2,⋯ ,xnx_1,x_2,\cdots,x_nx1​,x2​,⋯,xn​&#xff0c;以及 111 个整数 kkk&#xff08;k<nk<nk<n&#xff09;。从 nnn 个整数中任选 kkk 个整数相加&#xff0c;可分别得到一系列的和。例如当 n4n…

Linux学习记录(八)文件共享

本文记录在Vmware中启用文件共享时的一些注意事项&#xff1a;1.提前安装vmware-tools&#xff0c;可以通过Vmware的虚拟机菜单栏中拿到文件&#xff0c;然后直接运行vmware-install.pl文件进行安装&#xff1b;也可以通过指令sudo apt-get install open-vm-tools进行安装。推荐…

洛谷 火烧赤壁 差分/贪心

题目背景曹操平定北方以后&#xff0c;公元 208 年&#xff0c;率领大军南下&#xff0c;进攻刘表。他的人马还没有到荆州&#xff0c;刘表已经病死。他的儿子刘琮听到曹军声势浩大&#xff0c;吓破了胆&#xff0c;先派人求降了。孙权任命周瑜为都督&#xff0c;拨给他三万水军…

Linux 用户与组管理全解析

Linux 用户与组管理一、用户和组的基本概念 1. 用户账号类型 超级用户&#xff08;root&#xff09;&#xff1a;默认拥有系统最高权限&#xff08;UID0&#xff09;&#xff0c;仅建议用于系统管理与维护&#xff0c;日常操作应使用普通用户。普通用户&#xff1a;由管理员创建…

开疆智能ModbusTCP转Profient网关连接ER机器人配置案例

本案例时西门子1200PLC通过ModbusTCP转Profinet网关连接埃斯顿机器人的配置案例&#xff0c;网关作为ModbusTCP的客户端连接机器人。配置过程&#xff1a;首先打开机器人通讯手册。查询机器人支持的功能码及默认IP和端口号打开网关配置软件“Gateway Configuration Studio”新建…

Docker换源加速(更换镜像源)详细教程(2025.3最新可用镜像,全网最详细)

文章目录前言可用镜像源汇总换源方法1-临时换源换源方法2-永久换源&#xff08;推荐&#xff09;常见问题及对应解决方案1.换源后&#xff0c;可以成功pull&#xff0c;但是search会出错补充1.如何测试镜像源是否可用2.Docker内的Linux换源教程换源速通版&#xff08;可以直接无…

机器学习【三】SVM

本文系统介绍了支持向量机(SVM)的理论与实践。理论部分首先区分了线性可分与不可分问题&#xff0c;阐述了SVM通过寻找最优超平面实现分类的核心思想&#xff0c;包括支持向量、间隔最大化等关键概念。详细讲解了硬间隔与软间隔SVM的数学原理&#xff0c;以及核函数(线性核、多…

DevOps平台大比拼:Gitee、Jenkins与CircleCI如何选型?

DevOps平台大比拼&#xff1a;Gitee、Jenkins与CircleCI如何选型&#xff1f; 在数字化转型浪潮席卷全球的当下&#xff0c;DevOps已成为企业提升研发效能的关键引擎。面对市场上纷繁复杂的DevOps工具链&#xff0c;如何选择最适合自身业务需求的平台成为技术决策者的重要课题。…

开源医院信息管理系统:基于若依框架的智慧医疗解决方案

引言在数字化浪潮的推动下&#xff0c;医疗行业正加速向信息化、智能化转型。医院信息管理系统&#xff08;HIS&#xff09;作为医疗管理的核心工具&#xff0c;直接影响医院的运营效率和服务质量。近期&#xff0c;一款基于 若依框架 Vue 的开源医院管理系统&#xff08;hosp…

我的世界进阶模组开发教程——附魔(2)

EnchantmentHelper 类详解 EnchantmentHelper 是 Minecraft 中处理物品附魔逻辑的核心工具类,提供附魔的存储、查询、计算和应用等功能。以下是对其字段和方法的逐行详细解释: 关键字段 private static final String TAG_ENCH_ID = "id"; // NBT标签键:附…

深度学习零基础入门(4)-卷积神经网络架构

许久不见~ 本节我们延续上一节的话题来看看卷积神经网络的架构&#xff0c;看看具体的卷积、池化等操作卷积神经网络详解&#xff1a;从基础操作到整体架构 一、卷积操作&#xff1a;特征提取的核心 卷积是卷积神经网络&#xff08;CNN&#xff09;的核心操作&#xff0c;灵感来…

C语言的控制语句

C的控制语句 控制语句是C语言中用于控制程序执行流程的结构。通过控制语句,可以根据条件执行不同的代码块,或者重复执行某些操作,从而实现复杂的逻辑和功能。掌握控制语句是编写有效和高效C程序的关键。 1 条件控制 条件控制语句用于根据某些条件来决定程序的执行路径。C语…

Mac电脑基本功能快捷键

1. 个性化桌面 将喜爱照片添加为桌面墙纸。前往“系统设置”&#xff0c;然后点按边栏中的“墙纸”。点按“添加照片”&#xff0c;然后从文件或“照片”App选取一张照片。 2. 截屏 按下键盘上的Shift &#xfffc; Command ⌘ 5&#xff0c;然后选取捕捉整个屏幕、App窗口或…

微算法科技(NASDAQ: MLGO)开发量子边缘检测算法,为实时图像处理与边缘智能设备提供了新的解决方案

图像边缘检测是计算机视觉的核心任务&#xff0c;传统算法&#xff08;如 Sobel、Canny&#xff09;依赖梯度计算与阈值分割&#xff0c;在处理高分辨率、复杂纹理图像时面临计算效率瓶颈。随着量子计算技术的发展&#xff0c;利用量子态叠加与并行处理特性&#xff0c;微算法科…

断点续传Demo实现

基于我们的DownloadManager.swift代码&#xff0c;让我详细解释断点续传需要实现的核心功能&#xff1a; 断点续传的核心实现要素 1. 后台会话配置 private func setupBackgroundSession() {let config URLSessionConfiguration.background(withIdentifier: "com.test.do…

《Leetcode》-面试题-hot100-子串

题目列表 560. 和为K的子数组 中等难度 leetcode链接 239 滑动窗口最大值 困难难度 leetcode链接 76 最小覆盖子串 困难难度 leetcode链接 题目 &#xff08;1&#xff09;和为K的子数组 给你一个整数数组 nums 和一个整数 k &#xff0c;请你统计并返回 该数组中和为 …

点击弹框以外的区域关闭弹框

在 Vue 3 中&#xff0c;如果你想判断点击的目标是否在弹框内&#xff0c;可以通过以下步骤实现。这里我们将使用 ref 来引用弹框组件&#xff0c;并在点击事件中进行判断。 示例代码 1. 创建弹框子组件 首先&#xff0c;创建一个名为 Modal.vue 的子组件。 <!-- Modal.vue …

00.Vue基础入门【小白级别手把手!】

目录 一、Vue介绍 二、创建Vue项目 nodeJs nvm版本管理 创建Vue项目 VS Code编辑器 三、.Vue文件结构说明 数据渲染 四、Vue项目目录说明 main.ts文件说明 五、Vue官网文档学习 一、Vue介绍 基础介绍 Vue是一个前端Web框架&#xff0c;属于单页应用&#xff08;SPA&am…

将Varjo XR技术融入战斗机训练模拟器,有效提升模拟训练沉浸感与效率

本周在Varjo总部&#xff0c;收到了一份令人兴奋的礼物&#xff0c;一架由Dogfight Boss与varjo XR-4集成的训练模拟器。这是一个专业级模拟器&#xff0c;专为高保真训练和任务排练而设计&#xff0c;非常注重细节&#xff0c;提高了沉浸水平。为此Dogfight Boss的首席执行官L…

C# async await 实现机制详解

一、async/await 异步编程实现机制 1.1 核心概念 async/await 是 C# 5.0 引入的语法糖&#xff0c;它基于**状态机&#xff08;State Machine&#xff09;**模式实现&#xff0c;将异步方法转换为编译器生成的状态机类。 1.2 编译器转换过程 当编译器遇到 async 方法时&#xf…