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

序言

说到在 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), 对应着两种完全不同的使用场景。

  • MODE_STREAM:在这种模式下,通过 write 一次次把音频数据写到 AudioTrack 中。这和平时通过 write 调用往文件中写数据类似,但这种方式每次都需要把数据从用户提供的 Buffer 中拷贝到 AudioTrack 内部的 Buffer 中,在一定程度上会使引起延时。为解决这一问题,AudioTrack 就引入了第二种模式。
  • MODE_STATIC:在这种模式下,只需要在 play 之前通过一次 write 调用,把所有数据传递到 AudioTrack 中的内部缓冲区,后续就不必再传递数据了。这种模式适用于像铃声这种内存占用较小、延时要求较高的文件。但它也有一个缺点,就是一次 write 的数据不能太多,否则系统无法分配足够的内存来存储全部数据。

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

  • STREAM_ALARM:警告声
  • STREAM_MUSIC:音乐声,例如 music 等
  • STREAM_RING:铃声
  • STREAM_SYSTEM:系统声音,例如低电提示音,锁屏音等
  • STREAM_VOICE_CALL:通话声

注意:上面这些类型的划分和音频数据本身并没有关系。例如 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 上面,有需要的朋友可以参考一下。

转载于:https://juejin.im/post/5c9cc2c5f265da60e3470722

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android音视频开发是指在Android平台上进行音频和视频相关功能的开发。它涉及到音频的录制、播放,视频的采集、编码、解码和播放等方面。 在Android音视频开发中,可以使用Android提供的多媒体框架来实现各种功能。以下是Android音视频开发的一些关键点: 1. 音频开发: - 音频录制:可以使用AudioRecord类进行音频的录制,通过设置音频源、采样率、声道数等参数来实现。 - 音频播放:可以使用MediaPlayer类或AudioTrack类进行音频播放,通过设置音频文件路径或音频数据来实现。 2. 视频开发: - 视频采集:可以使用Camera类或Camera2 API进行视频的采集,通过设置摄像头参数、预览尺寸等来实现。 - 视频编码:可以使用MediaCodec类进行视频的编码,通过设置编码器类型、编码参数等来实现。 - 视频解码:可以使用MediaCodec类进行视频的解码,通过设置解码器类型、解码参数等来实现。 - 视频播放:可以使用SurfaceView或TextureView进行视频的播放,通过设置视频文件路径或视频数据来实现。 3. 音视频处理: - 音频处理:可以使用AudioEffect类进行音频的处理,如混音、变声等。 - 视频处理:可以使用OpenGL ES进行视频的处理,如滤镜、特效等。 4. 直播和推流: - 直播:可以使用第三方库,如librtmp、FFmpeg等来实现音视频的直播功能。 - 推流:可以使用第三方库,如librtmp、FFmpeg等来实现音视频的推流功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值