前阵子用一个JavaCV的FFmpeg库实现了YUV视频数据地采集,同样的采集PCM音频数据也可以采用JavaCV的FFmpeg库。

传送门:JavaCV FFmpeg采集摄像头YUV数据

首先引入 javacpp-ffmpeg依赖:

<dependency>
<groupId>org.bytedeco.javacpp-presets</groupId>
<artifactId>ffmpeg</artifactId>
<version>${ffmpeg.version}</version>
</dependency>
1. 查找麦克风设备

要采集麦克风的PCM数据,首先得知道麦克风的设备名称,可以通过FFmpeg来查找麦克风设备。

ffmpeg.exe -list_devices true -f dshow -i dummy

在我的电脑上结果显示如下:

其中 “麦克风阵列 (Realtek(R) Audio)” 就是麦克风的设备名称。(这里建议用耳麦[External Mic (Realtek(R) Audio)]录制,质量要好很多很多)

2. 利用FFmpeg解码

采集麦克风数据即将麦克风作为音频流输入,通过FFmpeg解码获取音频帧,然后将视频帧转为PCM格式,最后将数据写入文件即可,其实音频的解码过程跟视频的解码过程是几乎一致的,下面是FFmpeg音频的解码流程:

可以看出除了解码函数,音频解码流程和视频解码流程是一致的,音频解码调用的是avcodec_decode_audio4,而视频解码调用的是avcodec_decode_video2

3. 开发音频帧采集器

根据FFmpeg的解码流程,实现音频帧采集器大概需要经过以下几个步骤:

FFmpeg初始化

首先需要使用av_register_all()这个函数完成编码器和解码器的初始化,只有初始化了编码器和解码器才能正常使用;另外要采集的是设备,所以还需要调用avdevice_register_all()完成初始化。

分配AVFormatContext

接着需要分配一个AVFormatContext,可以通过avformat_alloc_context()来分配AVFormatContext。

pFormatCtx = avformat_alloc_context();

打开音频流

通过avformat_open_input()来打开音频流,这里需要注意的是input format要指定为dshow,可以通过av_find_input_format("dshow")获取AVInputFormat对象。

ret = avformat_open_input(pFormatCtx, String.format("audio=%s", input), av_find_input_format("dshow"), (AVDictionary) null);

注意:这里是音频用的是audio,不是video。

查找音频流

需要注意的是,查找音频流之前需要调用avformat_find_stream_info(),下面是查找视音频的代码:

ret = avformat_find_stream_info(pFormatCtx, (AVDictionary) null);
for (int i = 0; i < pFormatCtx.nb_streams(); i++) {
if (pFormatCtx.streams(i).codec().codec_type() == AVMEDIA_TYPE_AUDIO) {
audioIdx = i;
break;
}
}

打开解码器

可以通过音频流来查找解码器,然后打开解码器,对音频流进行解码,Java代码如下:

pCodecCtx = pFormatCtx.streams(audioIdx).codec();
pCodec = avcodec_find_decoder(pCodecCtx.codec_id());
if (pCodec == null) {
throw new FFmpegException("没有找到合适的解码器:" + pCodecCtx.codec_id());
}
// 打开解码器
ret = avcodec_open2(pCodecCtx, pCodec, (AVDictionary) null);
if (ret != 0) {
throw new FFmpegException(ret, "avcodec_open2 解码器打开失败");
}

采集音频帧

最后就是采集音频帧了,这里需要注意的是,如果向采集麦克风的音频流解码得到的是自己想要的格式,需要再次进行格式转化。

public AVFrame grab() throws FFmpegException {
if (av_read_frame(pFormatCtx, pkt) >= 0 && pkt.stream_index() == audioIdx) {
ret = avcodec_decode_audio4(pCodecCtx, pFrame, got, pkt);
if (ret < 0) {
throw new FFmpegException(ret, "avcodec_decode_audio4 解码失败");
}
if (got[0] != 0) {
return pFrame;
}
av_packet_unref(pkt);
}
return null;
}
4. 将音频帧数据写入文件

通过音频解码之后可以得到PCM数据,这里为了读取方便,我将音频数据转化为AV_SAMPLE_FMT_S16,即LRLRLR这种格式,而不是planar,这样子读取PCM数据的时候,只需要读取data[0]即可,下面是一段采集主程序,将采集的音频pcm数据写入到s16.pcm中:

public static void main(String[] args) throws FFmpegException, IOException {
FFmpegRegister.register();
// 耳机的麦克风质量要好得多
AudioGrabber a = AudioGrabber.create("External Mic (Realtek(R) Audio)");
// AV_SAMPLE_FMT_S16
AudioPCMWriter writer = null;
for (int i = 0; i < 100; i++) {
AVFrame f = a.grab();
if (writer == null) {
writer = AudioPCMWriter.create(new File("s16.pcm"), toChannelLayout(a.channels()), a.sample_fmt(), a.sample_rate(),
toChannelLayout(a.channels()), AV_SAMPLE_FMT_S16, a.sample_rate(), f.nb_samples());
}
writer.write(f);
}
writer.release();
a.release();
}
5. 播放采集的pcm数据

采集的pcm数据可以通过ffplay播放,命令如下:

ffplay.exe -ar 44100 -ac 2 -f s16le -i s16.pcm

播放的时候可以按“Q”退出:

当然如果不用ffplay来播放pcm,也可以自己写java程序来播放:

public static void main(String[] args) throws IOException, LineUnavailableException {
AudioPCMPlayer player = AudioPCMPlayer.create(2, AudioUtils.toBit(AV_SAMPLE_FMT_S16), 44100);
InputStream is = new FileInputStream("s16.pcm");
byte[] buff = new byte[4096];
int ret = -1;
while ((ret = is.read(buff)) != -1) {
if (ret < buff.length) {
break;
}
player.play(buff);
}
is.close();
player.release();
}

=========================================================

音频帧采集器、及pcm播放程序源码可关注公众号 “HiIT青年” 发送 “ffmpeg-pcm” 获取。



关注公众号,阅读更多文章。

JavaCV FFmpeg采集麦克风PCM音频数据的更多相关文章

  1. Android OpenSL ES 开发:Android OpenSL 录制 PCM 音频数据

    一.实现说明 OpenSL ES的录音要比播放简单一些,在创建好引擎后,再创建好录音接口基本就可以录音了.在这里我们做的是流式录音,所以需要用至少2个buffer来缓存录制好的PCM数据,这里我们可以 ...

  2. 使用AudioTrack播放PCM音频数据(android)

    众所周知,Android的MediaPlayer包含了Audio和video的播放功能,在Android的界面上,Music和Video两个应用程序都是调用MediaPlayer实现的.MediaPl ...

  3. JavaCV FFmpeg采集摄像头YUV数据

    前阵子使用利用树莓派搭建了一个视频监控平台(传送门),不过使用的是JavaCV封装好的OpenCVFrameGrabber和FFmpegFrameRecorder. 其实在javacpp项目集中有提供 ...

  4. HTML5操作麦克风获取音频数据(WAV)的一些基础技能

    基于HTML5的新特性,操作其实思路很简单. 首先通过navigator获取设备,然后通过设备监听语音数据,进行原始数据采集. 相关的案例比较多,最典型的就是链接:https://developer. ...

  5. JavaCV FFmpeg AAC编码

    上次成功通过FFmpeg采集麦克风的PCM数据,这次针对上一次的程序进行了改造,使用AAC编码采集后的数据. (传送门) JavaCV FFmpeg采集麦克风PCM音频数据 采集麦克风数据是一个解码过 ...

  6. DirectSound播放PCM(可播放实时采集的音频数据)

    前言 该篇整理的原始来源为http://blog.csdn.net/leixiaohua1020/article/details/40540147.非常感谢该博主的无私奉献,写了不少关于不同多媒体库的 ...

  7. Android 音视频开发(二):使用 AudioRecord 采集音频数据并保存到文件

    版权声明:转载请说明出处:http://www.cnblogs.com/renhui/p/7457321.html 一.AudioRecord API详解 AudioRecord是Android系统提 ...

  8. JavaCV 学习(二):使用 JavaCV + FFmpeg 制作拉流播放器

    一.前言 在 Android 音视频开发学习思路 中,我们不断的学习和了解音视频相关的知识,随着知识点不断的学习,我们现在应该做的事情,就是将知识点不断的串联起来.这样才能得到更深层次的领悟.通过整理 ...

  9. Android OpenSL ES 开发:OpenSL ES利用SoundTouch实现PCM音频的变速和变调

    缘由 OpenSL ES 学习到现在已经知道 OpenSL ES 不仅能播放和录制PCM音频数据,还能改变声音大小.设置左声道或右声道播放.还能变速播放,可谓是播放音频的王者.但是变速有一点不好的就是 ...

随机推荐

  1. Docker学习—Stack

    前言: 前一篇了解Docker使用Swarm集群部署方式,并创建服务到Swarm集群中:如果在集群部署过程中存在大量服务部署.编排那么该如何处理呢? 那么就需要了解Docker Stack了. 1.D ...

  2. 第二章、PyQt5应用构建详细过程介绍

    老猿Python博文目录 老猿Python博客地址 一.引言 在上节<第一章.PyQt的简介.安装与配置>介绍了PyQt5及其安装和配置过程,本节将编写一个简单的PyQt5应用,介绍基本的 ...

  3. 《深入理解计算机系统》(CSAPP)读书笔记 —— 第一章 计算机系统漫游

    本章通过跟踪hello程序的生命周期来开始对计算机系统进行学习.一个源程序从它被程序员创建开始,到在系统上运行,输出简单的消息,然后终止.我们将沿着这个程序的生命周期,简要地介绍一些逐步出现的关键概念 ...

  4. 题解-Cats Transport

    题解-Cats Transport Cats Transport 有 \(n\) 个山丘,\(m\) 只猫子,\(p\) 只铲屎官.第 \(i-1\) 个山丘到第 \(i\) 个山丘的距离是 \(d_ ...

  5. egg的基本使用

    一.脚手架(可以快速生成项目) 1.新建一个项目文件夹,使用如下命令: 2.npm  init  egg  --type=simple 3.npm i 它会根据package.json里记录的所需包进 ...

  6. Java集合源码分析(八)——WeakHashMap

    简介 WeakHashMap 继承于AbstractMap,实现了Map接口. 和HashMap一样,WeakHashMap 也是一个散列表,它存储的内容也是键值对(key-value)映射,而且键和 ...

  7. 调用windows系统下的cmd命令窗口处理文件

    从后缀名为grib2的文件中查询相关的信息,并将查出来的信息保存起来. 主要是学习java中调用windows下的cmd平台,并进行执行相关的命令. package com.wis.wgrib2; i ...

  8. 【python接口自动化】- 使用json及jsonpath转换和提取数据

    前言 ​ JSON(JavaScript Object Notation)是一种轻量级的数据交换格式.它可以让人们很容易的进行阅读和编写,同时也方便了机器进行解析和生成,适用于进行数据交互的场景,比如 ...

  9. 一篇文章掌握Nginx核心文件结构

    1 Nginx核心配置结构 2 配置模块详解 设置worker进程的用户,指的linux中的用户,会涉及到nginx操作目录或文件的一些权限,默认为nobody user root; worker进程 ...

  10. SpringBoot集成基于tobato的fastdfs-client实现文件上传下载和删除

    1. 简介   基于tobato的fastdfs-client是一个功能完善的FastDFS客户端工具,它是在FastDFS作者YuQing发布的客户端基础上进行了大量的重构,提供了上传.下载.删除. ...