类型

MediaPlayer:原生API中封装最全的

SoundPool:适合播放较短的音频

AudioTrack:底层的音频 API,需要自己解码,只能播放 PCM 裸数据和 WAV

AudioTrack

播放 PCM 音频裸数据,不能播放视频,需要自己先解码音频,接下来将说明 AudioTrack 初始化的几个参数的意义:

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat, int bufferSizeInBytes, int mode) throws IllegalArgumentException {

throw new RuntimeException("Stub!");

}

streamType 流类型,AudioManager 中定义了音频的类型,可大致分为 STREAM_MUSIC 、 STREAM_RINHG 等

sampleRateInHz 采样率,播放的音频每秒有多少次采样

channelConfig 声道数配置,单声道和双声道

audioFormat 数据位宽,选择 16bit ,能够兼容所有 Android 设备

bufferSizeInBytes 缓冲区大小,通过 AudioTrack.getMinBufferSize 运算得出

mode 播放模式 : MODE_STATIC 一次写入,MODE_STREAM 多次写入

播放 WAV 文件

WAV 是在 PCM 前面添加了几个字节来表示音频的格式,未对 PCM 进行压缩,所以文件体积相较于 MP3 等有损压缩文件要大,我们的 AudioTrack 可以播放 WAV 文件

实践

为了使用 AudioTrack 播放,我们需要提前准备一份 WAV 格式的音频文件,将文件重命名为 “test.wav”,然后放到手机内置存储的根目录中 ,使用流的形式播放,就需要将文件转成 InputStream,然后将 byte 数据 通过 AudioTrack.write() 写入缓冲区进行播放

准备读取 WAV 文件

public class WavFileHeader {

public static final int WAV_FILE_HEADER_SIZE = 44;

public static final int WAV_CHUNKSIZE_EXCLUDE_DATA = 36;

public static final int WAV_CHUNKSIZE_OFFSET = 4;

public static final int WAV_SUB_CHUNKSIZE1_OFFSET = 16;

public static final int WAV_SUB_CHUNKSIZE2_OFFSET = 40;

public String mChunkID = "RIFF";

public int mChunkSize = 0;

public String mFormat = "WAVE";

public String mSubChunk1ID = "fmt ";

public int mSubChunk1Size = 16;

public short mAudioFormat = 1;

public short mNumChannel = 1;

public int mSampleRate = 8000;

public int mByteRate = 0;

public short mBlockAlign = 0;

public short mBitsPerSample = 8;

public String mSubChunk2ID = "data";

public int mSubChunk2Size = 0;

public WavFileHeader() {

}

public WavFileHeader(int sampleRateInHz, int channels, int bitsPerSample) {

mSampleRate = sampleRateInHz;

mBitsPerSample = (short) bitsPerSample;

mNumChannel = (short) channels;

mByteRate = mSampleRate * mNumChannel * mBitsPerSample / 8;

mBlockAlign = (short) (mNumChannel * mBitsPerSample / 8);

}

}

使用 InputStream 将 Wav 文件转成流

public class WavFileReader {

private static final String TAG = WavFileReader.class.getSimpleName();

private DataInputStream mDataInputStream;

private WavFileHeader mWavFileHeader;

public boolean openFile(String filepath) throws IOException {

if (mDataInputStream != null) {

closeFile();

}

mDataInputStream = new DataInputStream(new FileInputStream(filepath));

return readHeader();

}

public void closeFile() throws IOException {

if (mDataInputStream != null) {

mDataInputStream.close();

mDataInputStream = null;

}

}

public WavFileHeader getmWavFileHeader() {

return mWavFileHeader;

}

public int readData(byte[] buffer, int offset, int count) {

if (mDataInputStream == null || mWavFileHeader == null) {

return -1;

}

try {

int nbytes = mDataInputStream.read(buffer, offset, count);

if (nbytes == -1) {

return 0;

}

return nbytes;

} catch (IOException e) {

e.printStackTrace();

}

return -1;

}

private boolean readHeader() {

if (mDataInputStream == null) {

return false;

}

WavFileHeader header = new WavFileHeader();

byte[] intValue = new byte[4];

byte[] shortValue = new byte[2];

try {

header.mChunkID = "" + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte();

Log.d(TAG, "Read file chunkID:" + header.mChunkID);

mDataInputStream.read(intValue);

header.mChunkSize = byteArrayToInt(intValue);

Log.d(TAG, "Read file chunkSize:" + header.mChunkSize);

header.mFormat = "" + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte();

Log.d(TAG, "Read file format:" + header.mFormat);

header.mSubChunk1ID = "" + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte();

Log.d(TAG, "Read fmt chunkID:" + header.mSubChunk1ID);

mDataInputStream.read(intValue);

header.mSubChunk1Size = byteArrayToInt(intValue);

Log.d(TAG, "Read fmt chunkSize:" + header.mSubChunk1Size);

mDataInputStream.read(shortValue);

header.mAudioFormat = byteArrayToShort(shortValue);

Log.d(TAG, "Read audioFormat:" + header.mAudioFormat);

mDataInputStream.read(shortValue);

header.mNumChannel = byteArrayToShort(shortValue);

Log.d(TAG, "Read channel number:" + header.mNumChannel);

mDataInputStream.read(intValue);

header.mSampleRate = byteArrayToInt(intValue);

Log.d(TAG, "Read samplerate:" + header.mSampleRate);

mDataInputStream.read(intValue);

header.mByteRate = byteArrayToInt(intValue);

Log.d(TAG, "Read byterate:" + header.mByteRate);

mDataInputStream.read(shortValue);

header.mBlockAlign = byteArrayToShort(shortValue);

Log.d(TAG, "Read blockalign:" + header.mBlockAlign);

mDataInputStream.read(shortValue);

header.mBitsPerSample = byteArrayToShort(shortValue);

Log.d(TAG, "Read bitspersample:" + header.mBitsPerSample);

header.mSubChunk2ID = "" + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte() + (char) mDataInputStream.readByte();

Log.d(TAG, "Read data chunkID:" + header.mSubChunk2ID);

mDataInputStream.read(intValue);

header.mSubChunk2Size = byteArrayToInt(intValue);

Log.d(TAG, "Read data chunkSize:" + header.mSubChunk2Size);

Log.d(TAG, "Read wav file success !");

} catch (Exception e) {

e.printStackTrace();

return false;

}

mWavFileHeader = header;

return true;

}

private static short byteArrayToShort(byte[] b) {

return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getShort();

}

private static int byteArrayToInt(byte[] b) {

return ByteBuffer.wrap(b).order(ByteOrder.LITTLE_ENDIAN).getInt();

}

}

public class AudioPlayer {

private static final String TAG = "AudioPlayer";

private static final int DEFAULT_STREAM_TYPE = AudioManager.STREAM_MUSIC;

private static final int DEFAULT_SAMPLE_RATE = 44100;

private static final int DEFAULT_CHANNEL_CONFIG = AudioFormat.CHANNEL_OUT_MONO;

private static final int DEFAULT_AUDIO_FORMAT = AudioFormat.ENCODING_PCM_16BIT;

private static final int DEFAULT_PLAY_MODE = AudioTrack.MODE_STREAM;

private volatile boolean mIsPlayStarted = false;

private AudioTrack mAudioTrack;

public boolean startPlayer() {

return startPlayer(DEFAULT_STREAM_TYPE, DEFAULT_SAMPLE_RATE, DEFAULT_CHANNEL_CONFIG, DEFAULT_AUDIO_FORMAT);

}

public boolean startPlayer(int streamType, int sampleRateInHz, int channelConfig, int audioFormat) {

if (mIsPlayStarted) {

Log.e(TAG, "Player already started !");

return false;

}

int bufferSizeInBytes = AudioTrack.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat);

if (bufferSizeInBytes == AudioTrack.ERROR_BAD_VALUE) {

Log.e(TAG, "Invalid parameter !");

return false;

}

Log.i(TAG, "getMinBufferSize = " + bufferSizeInBytes + " bytes !");

mAudioTrack = new AudioTrack(streamType, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes, DEFAULT_PLAY_MODE);

if (mAudioTrack.getState() == AudioTrack.STATE_UNINITIALIZED) {

Log.e(TAG, "AudioTrack initialize fail !");

return false;

}

mIsPlayStarted = true;

Log.i(TAG, "Start audio player success !");

return true;

}

public void stopPlayer() {

if (!mIsPlayStarted) {

return;

}

if (mAudioTrack.getPlayState() == AudioTrack.PLAYSTATE_PLAYING) {

mAudioTrack.stop();

}

mAudioTrack.release();

mIsPlayStarted = false;

Log.i(TAG, "Stop audio player success !");

}

public boolean play(byte[] audioData, int offsetInBytes, int sizeInBytes) {

if (!mIsPlayStarted) {

Log.e(TAG, "Player not started !");

return false;

}

if (mAudioTrack.write(audioData, offsetInBytes, sizeInBytes) != sizeInBytes) {

Log.e(TAG, "Could not write all the samples to the audio device !");

}

mAudioTrack.play();

Log.d(TAG, "OK, Played " + sizeInBytes + " bytes !");

return true;

}

}

开启播放线程

private Runnable pcmRunnable = new Runnable() {

@Override

public void run() {

byte[] buffer = new byte[SAMPLES_PER_FRAME * 2];

while (wavFileReader.readData(buffer, 0, buffer.length) > 0) {

audioPlayer.play(buffer, 0, buffer.length);

}

audioPlayer.stopPlayer();

try {

wavFileReader.closeFile();

} catch (IOException e) {

e.printStackTrace();

}

}

};

最终实现了 Wav 的播放,但是听起来怪怪的,不知道是读取的哪个参数不正确

相关链接

audiotrack android,Android 音视频渲染-AudioTrack 播放相关推荐

  1. Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放

    1. 实现效果 效果图中,视频没有铺满 是因为使用了ExoPlayer的RESIZE_MODE_FIT模式, 虽然使用RESIZE_MODE_FILL模式可以填充整个父布局,但是本Demo中使用的视频 ...

  2. Android FFmpeg 音视频开发教程

    LearnFFmpeg 项目地址:githubhaohao/LearnFFmpeg 简介: Android FFmpeg 音视频开发教程 更多:作者   提 Bug 标签: An Android FF ...

  3. Android OpenGL ES视频渲染(一)GLSurfaceView

    相关文章:Android OpenGL ES视频渲染(二)EGL+OpenGL Android中视频渲染有几种方式,之前的文章使用的是nativewindow(包括softwareRender).今天 ...

  4. Android WebRTC 音视频开发总结(一)

    2014-03-03 13:51 by Blacker, 5464 阅读, 7 评论, 收藏, 编辑 接触Android WebRTC有一段时间了,现在将研究过程中的一些经验和知识总结出来,希望大家有 ...

  5. 【音视频】ijkplayer播放器参数说明文档

    [音视频]ijkplayer播放器参数说明文档 pragma mark - IJKMediaPlayback #pragma mark 通知IJKMPMediaPlaybackIsPreparedTo ...

  6. webrtc简单案例——音视频采集和播放

    webrtc简单案例--音视频采集和播放 目录 打开摄像头并将画面显示到页面 打开麦克风并在页面播放捕获的声音 同时打开摄像头和麦克风,并在页面显示画面和播放捕获的声音 1. 打开摄像头并将画面显示到 ...

  7. [工具]利用EasyRTSPClient工具检查摄像机RTSP流不能播放原因以及排查音视频数据无法播放问题...

    出现问题 我们在做流媒体开发的过程中,进程会出现摄像机RTSP流莫名其妙无法播放的问题,而我们常用的vlc经常是直接弹出一个无法播放的提示框就完事了,没有说明出错的原因,或者在vlc的消息里面能看到日 ...

  8. WebRTC音视频采集和播放示例及MediaStream媒体流解析

    WebRTC音视频采集和播放示例及MediaStream媒体流解析 目录 示例代码--同时打开摄像头和麦克风,并在页面显示画面和播放捕获的声音 API解析 mediaDevices MediaStre ...

  9. html5仿抖音全屏播放,仿抖音视频全屏播放滑动切换

    1 前言 随着移动技术的快速迭代,数据流量费用的快速下降,视频.直播正成为全民的媒体盛宴,我司必然也不会缺席此次盛宴,这里讲述的是通过h5实现仿抖音视频全屏播放&滑动切换的效果,供我司直播鉴定 ...

最新文章

  1. java transient关键字_小伙子,你真的搞懂 transient 关键字了吗?
  2. 计算机系统的组成doc,《计算机系统的组成》.doc
  3. [Swift通天遁地]七、数据与安全-(19)使用Swift实现原生的SHA1加密
  4. Python剑指offer:分行从上到下打印二叉树
  5. skype for business 无法共享桌面、无法传输图片
  6. MySQL索引底层原理理解以及常见问题总结
  7. Vondrak滤波原理详解及Matlab实现
  8. VSCode自定义代码片段3——url大全
  9. linux 挂载u盘考试,Linux 挂载U盘,与解挂
  10. CKEditor设置背景图片及宽高
  11. 数据结构与算法之排序(冒泡、选择、插入、希尔、归并、快速)
  12. 测试开发面试-技术题持续累积
  13. office2016显示已经激活,但每次打开都跳出激活页面
  14. 平板电脑可以用手写的计算机软件,平板电脑可以当手写板(绘图板)用吗?
  15. 【修复收藏功能、更新登录接口】知识付费小程序、博客小程序、完整版开源源码、资源变现小程序
  16. vue项目强制清除页面缓存
  17. 小学计算机小知识,小学生电脑基础知识
  18. Maven-仓库概念,下载与配置
  19. 【支付宝小程序控制硬件①】 申请个人支付宝小程序开发的个人账户,说说那些睬坑日志,集成mqtt协议在支付宝小程序,实现基本通讯!
  20. 转载了别人的cocos2d-x的学习笔记

热门文章

  1. 操作系统_多级反馈队列调度算法
  2. SHA算法描述及实现
  3. Response Request 对象
  4. 斯人若遇彩虹,遇上方知有,征女
  5. 华为云助力龙岗产业创新人才双选会,为深圳市打造数字经济人才高地
  6. 力扣 -- 309. 最佳买卖股票时机含冷冻期
  7. c++ - 第18节 - 哈希
  8. C语言实验题――字符逆序
  9. html5播放m8u3,【甲减症状】甲减是怎么引起的_甲减的症状表现_甲减怎么治_百科-大众养生网...
  10. 计算机目前就业哪些专业就业比较好?