package com.tlinux.mp3playeraudiotrack;
import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.util.Log;
/**
* Created by tlinux on 18-8-11.
*/
public class SimpleAudioOutput {
private static final String TAG = "AudioOutputTrack";
public static final int SAMPLES_PER_FRAME = 2;
public static final int BYTES_PER_SAMPLE = 4;
public static final int BYTES_PRE_FRAME = SAMPLES_PER_FRAME * BYTES_PER_SAMPLE;
private AudioTrack mAudioTrack;
private int mFrameRate;
private int minBufferSize;
public SimpleAudioOutput() {
super();
}
public void start(int frameRate) {
stop();
mFrameRate = frameRate;
mAudioTrack = createAudioTrack(frameRate);
mAudioTrack.play();
}
public AudioTrack createAudioTrack(int frameRate) {
int minBufferSizeBytes = AudioTrack.getMinBufferSize(frameRate,
AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT);
Log.i(TAG, "AudioTrack.minBufferSize = " + minBufferSizeBytes
+ " bytes = " + (minBufferSizeBytes / BYTES_PRE_FRAME)
+ " frames");
int bufferSize = 8*minBufferSizeBytes/8;
minBufferSize = bufferSize;
int outputBufferSizeFrames = bufferSize/BYTES_PRE_FRAME;
Log.i(TAG, "actual bufferSize = " + bufferSize + " bytes = " + outputBufferSizeFrames + " frames");
AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC,mFrameRate,
AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT,
bufferSize,AudioTrack.MODE_STREAM);
Log.i(TAG, "created AudioTrack");
return player;
}
public int write(byte[] buffer, int offset, int length) {
return mAudioTrack.write(buffer,offset,length);
}
public void stop() {
if (mAudioTrack != null) {
mAudioTrack.stop();
}
}
public int getFrameRate() {
return mFrameRate;
}
public AudioTrack getAudioTrack() {
return mAudioTrack;
}
public int getMinBufferSize() {
return minBufferSize;
}
}
主要介绍AudioTrack的使用方法
AudioTrack player = new AudioTrack(AudioManager.STREAM_MUSIC,mFrameRate,
AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT,
bufferSize,AudioTrack.MODE_STREAM);
构造方法需要传递上面的几个参数
- AudioManager.STREAM_MUSIC 音频的STREAM_MUSIC,系统会对不同音频类型进行优先级管理,另外调节音量的时候也会根据不同的STREAM类型进行调节
- AudioFormat.CHANNEL_OUT_STEREO 表示立体声,支持左右声道
- AudioFormat.ENCODING_PCM_16BIT 编码格式,ENCODING_PCM_16BIT代表录制的时候每一个采样数据用16bit(2字节表示),编码格式占用的字节越多,声音越细腻
- bufferSize:使用 AudioTrack的getMinBufferSize(frameRate,
AudioFormat.CHANNEL_OUT_STEREO,AudioFormat.ENCODING_PCM_16BIT) 方法计算出来的,系统会根据要设置的帧率,通道数和编码格式计算出来一个最小支持的缓冲区大小(和系统先机制有关,后面文章会进行分析),创建AudioTrac时候,会根据传递的bufferSize创建缓冲区,注意如果传递的bufferSize小于getMinBufferSize计算出来的值,会创建不成功 - AudioTrack.MODE_STREAM 表示以流的方式操作AudioTrak,以流的方式要使用mAudioTrack.write(buffer,offset,length) 不断将数据写入.另外一种模式是MODE_STATIC,这种模式是直接将数据加载进内存,传递给AudioTrack处理,由于pcm数据占用内存比较大,这种模式只适合提示音等短音频
mAudioTrack.write(buffer,offset,length)
这就是流模式写入数据的方法,注意当底层提供的缓冲区写满后该方法会阻塞住(底层采用生产者消费者模型,当消费者进行一些消费后,buffer能够完全写入后返回)
AudioTrack就是这么简单生产消费者模型等操作都在底层完成,使用者不需要考虑.
下面介绍一下如何使用ffmpeg获取pcm裸音频数据
ffmpeg -i jiangzhende.mp3 -f s16le -ar 44100 -ac 2 -acodec pcm_s16le xxx.pcm
- -i 制定输入文件
- -f 指定输出编码格式为16byte小端格式
- -ar指定输出采样率
- -ac 指定输出通道数
- acodec 指定解码格式
- xxx.pcm 为输出文件
tlinux@tlinux:~/media$ ffmpeg -i jiangzhende.mp3 -f s16le -ar 44100 -ac 2 -acodec pcm_s16le xxx.pcm
ffmpeg version 3.0.7-0ubuntu0.16.10.1 Copyright (c) 2000-2017 the FFmpeg developers
built with gcc 6.2.0 (Ubuntu 6.2.0-5ubuntu12) 20161005
configuration: --prefix=/usr --extra-version=0ubuntu0.16.10.1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --cc=cc --cxx=g++ --enable-gpl --enable-shared --disable-stripping --disable-decoder=libopenjpeg --disable-decoder=libschroedinger --enable-avresample --enable-avisynth --enable-gnutls --enable-ladspa --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libmodplug --enable-libmp3lame --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-librubberband --enable-librtmp --enable-libschroedinger --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxvid --enable-libzvbi --enable-openal --enable-opengl --enable-x11grab --enable-libdc1394 --enable-libiec61883 --enable-libzmq --enable-frei0r --enable-chromaprint --enable-libx264
libavutil 55. 17.103 / 55. 17.103
libavcodec 57. 24.102 / 57. 24.102
libavformat 57. 25.100 / 57. 25.100
libavdevice 57. 0.101 / 57. 0.101
libavfilter 6. 31.100 / 6. 31.100
libavresample 3. 0. 0 / 3. 0. 0
libswscale 4. 0.100 / 4. 0.100
libswresample 2. 0.101 / 2. 0.101
libpostproc 54. 0.100 / 54. 0.100
[mp3 @ 0x55f314cdfcc0] Skipping 0 bytes of junk at 260803.
[mjpeg @ 0x55f314ce17e0] Changing bps to 8
Input #0, mp3, from 'jiangzhende.mp3':
Metadata:
encoder : Lavf56.4.101
album : 不要你为难
title : 讲真的
artist : 曾惜
album_artist : 曾惜
disc : 1
track : 2
Duration: 00:03:59.02, start: 0.025056, bitrate: 328 kb/s
Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 320 kb/s Stream #0:1: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 2000x2000 [SAR 72:72 DAR 1:1], 90k tbr, 90k tbn, 90k tbc
Metadata:
comment : Other
Output #0, s16le, to 'xxx.pcm':
Metadata:
track : 2
album : 不要你为难
title : 讲真的
artist : 曾惜
album_artist : 曾惜
disc : 1
encoder : Lavf57.25.100
Stream #0:0: Audio: pcm_s16le, 44100 Hz, stereo, s16, 1411 kb/s
Metadata:
encoder : Lavc57.24.102 pcm_s16le
Stream mapping:
Stream #0:0 -> #0:0 (mp3 (native) -> pcm_s16le (native))
Press [q] to stop, [?] for help
size= 41171kB time=00:03:58.99 bitrate=1411.2kbits/s speed= 475x
video:0kB audio:41171kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.000000%
Stream #0:0: Audio: mp3, 44100 Hz, stereo, s16p, 320 kb/s Stream #0:1: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 2000x2000 [SAR 72:72 DAR 1:1], 90k tbr, 90k tbn, 90k tbc
从ffmpeg的输出可以看出,原来的mp3音频采用44100HZ采样率,stereo立体声双通道,s16p编码格式,所以我们在进行pm3转pcm的时候尽量采用相同的采样率,通道数和编码格式,以保证转换出来的pcm音频不失真