Android音视频开发系列-音频播放方案选择

1,563 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第7天,点击查看活动详情

前言

在音视频开发中播放视频之中除了视频画面外还有声音音频的播放。例如在MediaPlayer中内部是有单独处理音频播放逻辑的。在多媒体领域中音频也是主流媒体资源,同时也是Android音视频开发的基础内容。在Android开发中底层音频系统也提供了许多API来帮助开发者去实现音频播放功能。

音频播放方式

Android系统中提供了多种用来播放音频的方法类。但事实上绝大多数方法并不适用大部分场景,例如播放音乐或者是常用格式等有些方法可能无法生效。

方式简介
MediaPlayer支持AAC、AMR、FLAC、MP3、MIDI、OGG、PCM等格式
SoundPool支持多个音频同时播放、延时短;适用于短暂、密集场景下播放音效音频资源
AudioTrack比较偏向底层的形式(MediaPlayerService内部采用的是AudioTrack)
AsyncPlayer是MediaPlayer封装的异步音频播放器
JetPlayer常用于控制游戏声音特效,可播MIDI格式
Ringtone适用于铃声、通知以及其他声音播放

MadiaPlayer

MadiaPlayer方式就不多介绍在之前文章中就有提及MediaPlayer使用,音频使用方式和加载视频方式类似,唯一不同点可能在于音频播放不会使用到Surface而已。

SoundPool

SoundPool支持同时播放多个音频资源。从API提供的方法上看确实如此,SoundPool加载音频文件后会返回一个soundId,播放是通过soundId来执行;暂停、停止播放也是如此需要soundId作为入参才可以。但需要注意的是SoundPool只支持短促音频文件,播放时长不能太长。

//初始化SoundPool环境
AudioAttributes abs = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
        .build() ;
SoundPool mSoundPoll =  new SoundPool.Builder()
        .setMaxStreams(100) 
        .setAudioAttributes(abs)  
        .build();
//加载播放文件并拿到Id        
int soundId = mSoundPoll.load("sdcard/compat-res/3-48k.mp3",0);
//播放资源
mSoundPoll.play(soundId,1f,1f,10,0,1f);

测试播放一首歌曲发现只播放了前几秒声音,可见SoundPool确实只支持较小的音频文件。

AudioTrack

AudioTrack使用上比较偏向底层形式。可用于单个音频播放和管理,相比较来说使用AudioTrack更加高效精炼,更适用于为音频播放而采用。AudioTrack支持原始音频数据播放,若要支持其他格式音频需要自行实现格式解码能力,因此对于使用门槛来说稍微有点高。

new Thread(new Runnable() {
    @Override
    public void run() {
        // 获取最小缓冲区
        int bufSize = AudioTrack.getMinBufferSize(44100, AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_MP3);
        // 实例化AudioTrack(设置缓冲区为最小缓冲区的2倍,至少要等于最小缓冲区)
        AudioTrack audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 44100, AudioFormat.CHANNEL_OUT_STEREO,
                AudioFormat.ENCODING_MP3, bufSize*2, AudioTrack.MODE_STREAM);
        // 设置音量
        audioTrack.setVolume(2f) ;
        // 设置播放频率
        audioTrack.setPlaybackRate(10) ;
        audioTrack.play();
        // 获取音乐文件输入流
        try {
        InputStream is = new FileInputStream(file);
        byte[] buffer = new byte[bufSize*2] ;
        int len ;
            while((len=is.read(buffer,0,buffer.length)) != -1){
                System.out.println("读取数据中...");
                // 将读取的数据,写入Audiotrack
                audioTrack.write(buffer,0,buffer.length) ;
            }
            is.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}).start();

如上代码所示播放mp3格式文件并不生效。或许需要替换成PCM音频或许就可以正常播放。

AsyncPlayer

AsyncPlayer内部实际上是MediaPlayer简单封装,支持简单音频文件播放。只适用于简单异步播放,不支持控制进度,只支持播放和停止。

AsyncPlayer asyncPlayer = new AsyncPlayer("lkk");
Uri uri = Uri.fromFile(file);
asyncPlayer.play(this,uri,false,100);

JetPlayer

JetPlayer只支持特定格式音频文件,不属于通用播放音频方案。

JetPlayer jetPlayer = JetPlayer.getJetPlayer();
jetPlayer.clearQueue();
jetPlayer.loadJetFile("sdcard/compat-res/3-48k.mp3");
byte sSegmentID = 0 ;
//指定播放序列
jetPlayer.queueJetSegment(0, 0, 0, 0, 0, sSegmentID);
jetPlayer.queueJetSegment(1, 0, 1, 0, 0, sSegmentID);
jetPlayer.play();

Ringtone

Ringtone可为铃声、通知以及其他声音提供播放服务。

Uri uri = Uri.fromFile(file);
Ringtone ringtone = RingtoneManager.getRingtone(this,uri);
ringtone.play();

在测试过程中发现使用Ringtone可以播放歌曲文件;支持同时播放多个音频;

总结

对于选取哪种音频播放方案大方向取决于播放什么样类型的音频文件。

  • SoundPool适用于简单播放、延迟小、声音较短
  • AsyncPlayer适用在简单播放、没有负责操作中
  • AudioTrack则支持无压缩大文件以及流式播放
  • Ringtone比较适用于系统类型提醒音效播放 总体来说最终适用哪种音频播放方法还是需要看具体场景选择,自定义要求越高越需要开发者采用更底层方案自行设计实现。