AudioStream類
AudioStream類是音頻流的基類,重寫了MediaStream的encodeWithMediaRecorder方法,實(shí)現(xiàn)了MediaRecorder錄制音頻的操作芥驳。同時(shí)作為抽象類申屹,它并沒有重寫encodeWithMediaCodec方法溉苛,而是留給子類去具體實(shí)現(xiàn)。
@Override
protected void encodeWithMediaRecorder() throws IOException {
// We need a local socket to forward data output by the camera to the packetizer
createSockets();
Log.v(TAG,"Requested audio with "+mQuality.bitRate/1000+"kbps"+" at "+mQuality.samplingRate/1000+"kHz");
mMediaRecorder = new MediaRecorder();
mMediaRecorder.setAudioSource(mAudioSource);
mMediaRecorder.setOutputFormat(mOutputFormat);
mMediaRecorder.setAudioEncoder(mAudioEncoder);
mMediaRecorder.setAudioChannels(1);
mMediaRecorder.setAudioSamplingRate(mQuality.samplingRate);
mMediaRecorder.setAudioEncodingBitRate(mQuality.bitRate);
// We write the ouput of the camera in a local socket instead of a file !
// This one little trick makes streaming feasible quiet simply: data from the camera
// can then be manipulated at the other end of the socket
mMediaRecorder.setOutputFile(mSender.getFileDescriptor());
mMediaRecorder.prepare();
mMediaRecorder.start();
try {
// mReceiver.getInputStream contains the data from the camera
// the mPacketizer encapsulates this stream in an RTP stream and send it over the network
mPacketizer.setDestination(mDestination, mRtpPort, mRtcpPort);
mPacketizer.setInputStream(mReceiver.getInputStream());
mPacketizer.start();
mStreaming = true;
} catch (IOException e) {
stop();
throw new IOException("Something happened with the local sockets :/ Start failed !");
}
}
重寫encodeWithMediaRecorder方法贺喝,主要是MediaRecorder錄制音頻的操作菱鸥。首先啟動(dòng)本地Socket,再對(duì)MediaRecorder對(duì)象設(shè)置音頻來源躏鱼、音頻編碼氮采、音頻質(zhì)量等參數(shù),然后開始錄制染苛。這里值得一提的是鹊漠,MediaRecorder輸出的音頻數(shù)據(jù)是寫入Socket中而不是文件中,這樣就可以在Socket的另一端進(jìn)行操作茶行。最后一步是使用Packetizer對(duì)象將數(shù)據(jù)打包并發(fā)送到網(wǎng)絡(luò)傳輸躯概。
AACStream類
AACStream類是一個(gè)關(guān)于AAC音頻格式的AudioStream的子類,內(nèi)部封裝了對(duì)AAC音頻格式的配置和編碼操作拢军。
private static boolean AACStreamingSupported() {
if (Build.VERSION.SDK_INT<14) return false;
try {
MediaRecorder.OutputFormat.class.getField("AAC_ADTS");
return true;
} catch (Exception e) {
return false;
}
}
構(gòu)造函數(shù)中會(huì)判斷是否支持AAC編碼格式楞陷。
// Checks if the user has supplied an exotic sampling rate
int i=0;
for (;i<AUDIO_SAMPLING_RATES.length;i++) {
if (AUDIO_SAMPLING_RATES[i] == mQuality.samplingRate) {
mSamplingRateIndex = i;
break;
}
}
// If he did, we force a reasonable one: 16 kHz
if (i>12) mQuality.samplingRate = 16000;
configure()方法中的部分代碼。在start()開始時(shí)需要先檢查一下采樣率配置信息茉唉,不符合規(guī)范則強(qiáng)行設(shè)置默認(rèn)采樣率固蛾。
@Override
protected void encodeWithMediaRecorder() throws IOException {
testADTS();
((AACADTSPacketizer)mPacketizer).setSamplingRate(mQuality.samplingRate);
super.encodeWithMediaRecorder();
}
重寫encodeWithMediaRecorder方法,testADTS()方法是先從麥克風(fēng)記錄AAC ADTS的簡短樣本度陆,以了解該設(shè)備支持的真實(shí)的采樣率艾凯,便于設(shè)置配置信息和防止報(bào)錯(cuò)。篇幅原因懂傀,這里就不貼出testADTS()的代碼了趾诗。
下面我們開始分析重寫encodeWithMediaCodec()方法里面的內(nèi)容,我把逐句分析寫在注釋里面蹬蚁。
//計(jì)算出緩沖區(qū)的大小
final int bufferSize = AudioRecord.getMinBufferSize(mQuality.samplingRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)*2;
//設(shè)置打包器的采樣率
((AACLATMPacketizer)mPacketizer).setSamplingRate(mQuality.samplingRate);
//實(shí)例化AudioRecord恃泪,參數(shù)依次為:聲音來源、采樣率犀斋、聲道數(shù)贝乎、編碼方式、緩沖區(qū)
mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, mQuality.samplingRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
//實(shí)例化MediaCodec編碼器
mMediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm");
//配置信息依次為:格式叽粹、位速率览效、頻道數(shù)、采樣率虫几、AAC文件锤灿、最大輸入緩沖區(qū)
MediaFormat format = new MediaFormat();
format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm");
format.setInteger(MediaFormat.KEY_BIT_RATE, mQuality.bitRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_SAMPLE_RATE, mQuality.samplingRate);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSize);
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
//開始錄制音頻
mAudioRecord.startRecording();
mMediaCodec.start();
//設(shè)置編碼流,在后面的文章會(huì)詳細(xì)講到
final MediaCodecInputStream inputStream = new MediaCodecInputStream(mMediaCodec);
final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers();
//開啟一個(gè)線程辆脸,讀取和處理
mThread = new Thread(new Runnable() {
@Override
public void run() {
int len = 0, bufferIndex = 0;
try {
//無限循環(huán)讀取
while (!Thread.interrupted()) {
//從輸入流隊(duì)列中取數(shù)據(jù)進(jìn)行編碼操作(出隊(duì)列)但校。
bufferIndex = mMediaCodec.dequeueInputBuffer(10000);
if (bufferIndex>=0) {
inputBuffers[bufferIndex].clear();
//從mAudioRecord讀取數(shù)據(jù)到inputBuffers[bufferIndex]中
len = mAudioRecord.read(inputBuffers[bufferIndex], bufferSize);
if (len == AudioRecord.ERROR_INVALID_OPERATION || len == AudioRecord.ERROR_BAD_VALUE) {
Log.e(TAG,"An error occured with the AudioRecord API !");
} else {
//Log.v(TAG,"Pushing raw audio to the decoder: len="+len+" bs: "+inputBuffers[bufferIndex].capacity());
//輸入流入隊(duì)列(往編碼器中添加數(shù)據(jù)做編碼處理)
mMediaCodec.queueInputBuffer(bufferIndex, 0, len, System.nanoTime()/1000, 0);
}
}
}
} catch (RuntimeException e) {
e.printStackTrace();
}
}
});
mThread.start();
//把編碼完成的數(shù)據(jù)流封裝打包并進(jìn)行網(wǎng)絡(luò)傳輸
// The packetizer encapsulates this stream in an RTP stream and send it over the network
mPacketizer.setDestination(mDestination, mRtpPort, mRtcpPort);
mPacketizer.setInputStream(inputStream);
mPacketizer.start();
mStreaming = true;
以上可以看到,重寫encodeWithMediaCodec()方法主要流程就是:實(shí)例化和配置AudioRecord用來錄取音頻數(shù)據(jù)啡氢,實(shí)例化和配置MediaCodec用于對(duì)數(shù)據(jù)編碼状囱,開啟一個(gè)線程循環(huán)讀取AudioRecord錄取的音頻數(shù)據(jù)流州刽,并將原始音頻數(shù)據(jù)流添加到MediaCodec編碼器中進(jìn)行編碼,然后將編碼完成的數(shù)據(jù)流通過Packetizer打包器打包并發(fā)送出去浪箭。
AMRNBStream類
AMRNBStream類是一個(gè)關(guān)于ANR音頻格式的AudioStream的子類穗椅。
@Override
protected void encodeWithMediaCodec() throws IOException {
super.encodeWithMediaRecorder();
}
AMRNBStream可操作的動(dòng)作不多,這里重寫encodeWithMediaCodec()方法直接指向了super.encodeWithMediaRecorder()奶栖。關(guān)于AAC和AMR的比較可參考:AAC和AMR音頻編碼標(biāo)準(zhǔn)介紹
至此我們完整的了解了音頻數(shù)據(jù)流的配置匹表、采集、編碼的整個(gè)過程宣鄙,下一篇我們將分析VideoStream類和它的子類袍镀,詳細(xì)了解視頻流的配置、采集和編碼的流程冻晤。