JavaCV FFmpeg采集麥克風PCM音頻數據


前陣子用一個JavaCV的FFmpeg庫實現了YUV視頻數據地采集,同樣的采集PCM音頻數據也可以采用JavaCV的FFmpeg庫。

傳送門:JavaCV FFmpeg采集攝像頭YUV數據

其他關於JavaCV的文章,可以通過下面的鏈接查看:
JavaCV-開發系列文章匯總篇(https://www.cnblogs.com/itqn/p/14696221.html)

首先引入 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” 獲取。

HiIT青年
關注公眾號,閱讀更多文章。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM