问题
如何实现录音,并获取pcm数据
如何上传(要求用二进制流的content-type上传pcm数据):Content-Type: application/octet-stream
获取到服务器的音频信息之后如何播放(音频可能有两种,一种url,一种pcm数据流)
实现录音,存成pcm数据 有两个选择:
区别:
medioRecord相对audioRecord更加上层一点,底层使用的是audioRecord
medioRecord会把对音频数据做一些编码相关的处理,可以通过它获取到可以直接播放的音频文件,具体格式参见tip1,而audioRecord获取到的是pcm格式数据,该格式数据不能直接播放
audioRecord具有实时性
结论:
因为录制的音频要上传给服务端,而且服务端要求pcm流数据,所有选择audioRecord实现。
注意
客户端跟服务端的pcm数据格式一定要一致(采样率,声道等)(坑了我好久。。。)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 package com.example.mylibrary.audio;import android.media.AudioFormat;import android.media.AudioRecord;import android.media.MediaRecorder;import android.os.Environment;import java.io.BufferedOutputStream;import java.io.File;import java.io.FileNotFoundException;import java.io.FileOutputStream;import java.io.IOException;import java.io.OutputStream;public class AudioRecordUtil { private static AudioRecordUtil mInstance; private AudioRecord recorder; private static int audioSource = MediaRecorder.AudioSource.MIC; private static int audioRate = 16000 ; private static int audioChannel = AudioFormat.CHANNEL_IN_DEFAULT; private static int audioFormat = AudioFormat.ENCODING_PCM_16BIT; private static int bufferSize = AudioRecord.getMinBufferSize(audioRate, audioChannel, audioFormat); private boolean isRecording = false ; private byte [] noteArray; private File pcmFile; private File wavFile; private OutputStream os; private String basePath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/xiaoIceStory/" ; private String inFileName = basePath + "/encode.pcm" ; public String getPcmFilePath () { return inFileName; } private AudioRecordUtil () { createFile(); } private void createFile () { File baseFile = new File(basePath); if (!baseFile.exists()) { baseFile.mkdirs(); } pcmFile = new File(basePath + "/encode.pcm" ); if (pcmFile.exists()) { pcmFile.delete(); } try { pcmFile.createNewFile(); } catch (IOException e) { e.printStackTrace(); } } public static AudioRecordUtil getInstance () { if (mInstance == null ) { synchronized (AudioRecordUtil.class){ if (mInstance==null ){ mInstance = new AudioRecordUtil(); } } } return mInstance; } class WriteThread implements Runnable { @Override public void run () { writeData(); } } private void writeData () { noteArray = new byte [bufferSize]; try { os = new BufferedOutputStream(new FileOutputStream(pcmFile)); } catch (FileNotFoundException e) { e.printStackTrace(); } while (isRecording) { int recordSize = recorder.read(noteArray, 0 , bufferSize); if (recordSize > 0 ) { try { os.write(noteArray); } catch (IOException e) { e.printStackTrace(); } } } if (os != null ) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } public void startRecord () { isRecording = true ; recorder = new AudioRecord(audioSource, audioRate, audioChannel, audioFormat, bufferSize); recorder.startRecording(); } public void recordData () { new Thread(new WriteThread()).start(); } public void stopRecord () { if (recorder != null ) { isRecording = false ; recorder.stop(); recorder.release(); } } }
参考:
https://developer.android.com/reference/android/media/MediaRecorder
https://developer.android.com/guide/topics/media/mediarecorder
https://www.jianshu.com/p/90c4071c7768
http://www.cnblogs.com/renhui/p/7463287.html
实现上传音频pcm数据流
1 2 3 4 public static RequestBody create (final @Nullable MediaType contentType, final byte [] content) { return create(contentType, content, 0 , content.length); }
因为服务端对requestBody格式有要求:
1 2 3 4 5 6 7 8 9 10 11 12 --this -is-a-boundary Content-Disposition: form-data; name="metadata" Content-Type: application/json; charset=UTF-8 {xxx这是一段json格式的数据xxx} --this -is-a-boundary Content-Disposition: form-data; name="audio" Content-Type: application/octet-stream pcm数据 --this -is-a-boundary--
所以采用如下方式:
1 2 @POST ()Flowable<ResponseBody> uploadPcm (@Url String url, @Body RequestBody body, @Header("xxx" ) String appId) ;
其中的body如下:
1 2 requestBody = RequestBody.create(MediaType.parse("*/*;charset=utf-8" ), contentBytes, 0 , contentBytes.length);
播放pcm数据使用AudioTrack 注意pcm数据格式需要跟服务端的一致:声道,采样率等。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 package com.ms.xiaoicestorydemo.util;import android.media.AudioFormat;import android.media.AudioManager;import android.media.AudioTrack;import java.io.IOException;import java.io.RandomAccessFile;public class AudioPlayUtils { private final int BUFFER_SIZE = 1024 * 2 ; private byte [] mBuffer = new byte [BUFFER_SIZE]; private AudioPlayUtils () { } private static AudioPlayUtils instance; public static AudioPlayUtils getInstance () { if (instance == null ) { synchronized (AudioPlayUtils.class) { if (instance == null ) { instance = new AudioPlayUtils(); } } } return instance; } public void playPcmFileAudio (RandomAccessFile raf) { int streamType = AudioManager.STREAM_MUSIC; int simpleRate = 16000 ; int channelConfig = AudioFormat.CHANNEL_IN_DEFAULT; int audioFormat = AudioFormat.ENCODING_PCM_16BIT; int mode = AudioTrack.MODE_STREAM; int minBufferSize = AudioTrack.getMinBufferSize(simpleRate, channelConfig, audioFormat); AudioTrack audioTrack = new AudioTrack(streamType, simpleRate, channelConfig, audioFormat, Math.max(minBufferSize, BUFFER_SIZE), mode); audioTrack.play(); try { int read; while ((read = raf.read(mBuffer)) > 0 ) { audioTrack.write(mBuffer, 0 , read); } } catch (RuntimeException | IOException e) { e.printStackTrace(); } } }
播放在线音频 使用exoMedia:git地址:https://github.com/brianwernick/ExoMedia
其中使用的是exoPlayer:git地址:https://github.com/google/ExoPlayer
tip1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static final int DEFAULT = 0 ; public static final int THREE_GPP = 1 ; public static final int MPEG_4 = 2 ; public static final int RAW_AMR = 3 ; public static final int AMR_NB = 3 ; public static final int AMR_WB = 4 ; public static final int AAC_ADIF = 5 ; public static final int AAC_ADTS = 6 ; public static final int OUTPUT_FORMAT_RTP_AVP = 7 ; public static final int MPEG_2_TS = 8 ; public static final int WEBM = 9 ; };