Android开发Android技术知识Android进阶之路

Android 音视频开发 - 使用AudioTrack播放音频

2019-02-22  本文已影响3人  Android架构

序言

说到在 Android 平台上播放音频,我们最先想到的是 MediaPlayer,系统 API 对其做了比较全面的封装,开发者用少量的代码就能实现播放功能。MediaPlayer 可以播放多种格式的声音文件,例如 MP3,AAC,WAV,OGG,MIDI 等,而 AudioTrack 只能播放 PCM 数据流。

实际上,MediaPlayer 在播放音频时,在 Framework 层还是会创建 AudioTrack,把解码后的 PCM 数流传递给 AudioTrack,最后由 AudioFlinger 进行混音,把音频传递给硬件播放出来。利用 AudioTrack 播放只是跳过 Mediaplayer 的解码部分而已。如果是实时的音频数据,那么只能用 AudioTrack 进行播放。

AudioTrack 有两种数据加载模式(MODE_STREAM 和 MODE_STATIC), 对应着两种完全不同的使用场景。

在 AudioTrack 构造函数中,会接触到 AudioManager.STREAM_MUSIC 这个参数。它的含义与 Android 系统对音频流的管理和分类有关。Android 将系统的声音分为好几种流类型,下面是几个常见的:

注意:上面这些类型的划分和音频数据本身并没有关系。例如 MUSIC 和 RING 类型都可以是某首 MP3 歌曲。另外,声音流类型的选择没有固定的标准,例如,铃声预览中的铃声可以设置为 MUSIC 类型。音频流类型的划分和 Audio 系统对音频的管理策略有关。

我们用代码实践一下播放的流程

  1. 创建播放对象,参数和 AudioRecord 有相似之处。
    public void createAudioTrack(String filePath) {
        mFilePath = filePath;
        mBufferSizeInBytes = AudioTrack.getMinBufferSize(AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING);
        mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, AUDIO_SAMPLE_RATE, AUDIO_CHANNEL, AUDIO_ENCODING,
                mBufferSizeInBytes, AudioTrack.MODE_STREAM);
        mStatus = Status.STATUS_READY;
    }

  1. 开始播放,不断从文件中读数据,然后向 Buffer 里面写数据。
    public void start() {
        if (mStatus == Status.STATUS_NO_READY || mAudioTrack == null) {
            throw new IllegalStateException("播放器尚未初始化");
        }
        if (mStatus == Status.STATUS_START) {
            throw new IllegalStateException("正在播放...");
        }
        Log.d(TAG, "===start===");
        mExecutorService.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    writeAudioData();
                } catch (IOException e) {
                    Log.e(TAG, e.getMessage());
                    mMainHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            Toast.makeText(mContext, "播放出错", Toast.LENGTH_SHORT).show();
                        }
                    });
                }
            }
        });
        mStatus = Status.STATUS_START;
    }

    private void writeAudioData() throws IOException {
        DataInputStream dis = null;
        try {
            mMainHandler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "播放开始", Toast.LENGTH_SHORT).show();
                }
            });
            FileInputStream fis = new FileInputStream(mFilePath);
            dis = new DataInputStream(new BufferedInputStream(fis));
            byte[] bytes = new byte[mBufferSizeInBytes];
            int len;
            mAudioTrack.play();
            while ((len = dis.read(bytes)) != -1 && mStatus == Status.STATUS_START) {
                mAudioTrack.write(bytes, 0, len);
            }
            mMainHandler.post(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mContext, "播放结束", Toast.LENGTH_SHORT).show();
                }
            });
        } finally {
            if (dis != null) {
                dis.close();
            }
        }
    }

  1. 停止播放,释放资源。
    public void stop() {
        Log.d(TAG, "===stop===");
        if (mStatus == Status.STATUS_NO_READY || mStatus == Status.STATUS_READY) {
            throw new IllegalStateException("播放尚未开始");
        } else {
            mAudioTrack.stop();
            mStatus = Status.STATUS_STOP;
            release();
        }
    }

    public void release() {
        Log.d(TAG, "==release===");
        if (mAudioTrack != null) {
            mAudioTrack.release();
            mAudioTrack = null;
        }
        mStatus = Status.STATUS_NO_READY;
    }

具体代码在 GitHub 上面,有需要的朋友可以参考一下。
【附录】

资料图

需要资料的朋友可以加入Android架构交流QQ群聊:513088520

点击链接加入群聊【Android移动架构总群】:加入群聊

获取免费学习视频,学习大纲另外还有像高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)等Android高阶开发资料免费分享。

上一篇 下一篇

猜你喜欢

热点阅读