package com.jb.vmeeting.tools.streaming.audio; import android.annotation.TargetApi; import android.media.AudioFormat; import android.media.AudioRecord; import android.media.MediaCodec; import android.media.MediaCodecInfo; import android.media.MediaFormat; import android.media.MediaRecorder; import android.os.Build; import android.util.Log; import com.jb.vmeeting.tools.L; import com.jb.vmeeting.tools.streaming.rtp.MediaCodecInputStream; import com.jb.vmeeting.tools.streaming.rtp.packetizer.AACLATMPacketizer; import java.io.IOException; import java.nio.ByteBuffer; /** * Created by Jianbin on 2016/3/26. */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) public class AACStream extends AudioStream { protected MediaCodec mMediaCodec; private String mSessionDescription = null; private AudioRecord mAudioRecord = null; private Thread mThread = null; public AACStream() { super(); //TODO check aac stream supported or not // if (!AACStreamingSupported()) { // L.e("AAC not supported on this phone"); // throw new RuntimeException("AAC not supported by this phone !"); // } else { // L.d("AAC supported on this phone"); // } } @Override public void configure() throws IllegalStateException, IOException { super.configure(); // 新建对应的Packetizer,并初始化SessionDescription // 或者采用设置Packetizer的方式? mPacketizer = new AACLATMPacketizer(); int mProfile = 2; // AAC LC int mChannel = 1; int mSamplingRateIndex = 11; // 8000 rate int mConfig = mProfile<<11 | mSamplingRateIndex<<7 | mChannel<<3; mSessionDescription = "m=audio "+String.valueOf(getDestinationPorts()[0])+" RTP/AVP 96\r\n" + "a=rtpmap:96 mpeg4-generic/"+8000/*mQuality.samplingRate*/+"\r\n"+ "a=fmtp:96 streamtype=5; profile-level-id=15; mode=AAC-hbr; config="+Integer.toHexString(mConfig)+"; SizeLength=13; IndexLength=3; IndexDeltaLength=3;\r\n"; } @Override public synchronized void start() throws IllegalStateException, IOException { if (!mStreaming) { super.start(); } } @Override protected void encode() throws IOException { // 获取音频数据,并对其进行编码,之后传递给packetizer进行打包发送 // 或者采用设置编码器的方式? final int bufferSize = AudioRecord.getMinBufferSize(8000/*mQuality.samplingRate*/, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT)*2; ((AACLATMPacketizer)mPacketizer).setSamplingRate(8000/*mQuality.samplingRate*/); mAudioRecord = new AudioRecord(MediaRecorder.AudioSource.MIC, 8000/*mQuality.samplingRate*/, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize); mMediaCodec = MediaCodec.createEncoderByType("audio/mp4a-latm"); MediaFormat format = new MediaFormat(); format.setString(MediaFormat.KEY_MIME, "audio/mp4a-latm"); format.setInteger(MediaFormat.KEY_BIT_RATE,32000 /*mQuality.bitRate*/); format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1); format.setInteger(MediaFormat.KEY_SAMPLE_RATE, 8000/*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(); final MediaCodecInputStream inputStream = new MediaCodecInputStream(mMediaCodec); final ByteBuffer[] inputBuffers = mMediaCodec.getInputBuffers(); mThread = new Thread(new Runnable() { @Override public void run() { int len = 0, bufferIndex = 0; try { while (!Thread.interrupted()) { bufferIndex = mMediaCodec.dequeueInputBuffer(10000); if (bufferIndex>=0) { inputBuffers[bufferIndex].clear(); len = mAudioRecord.read(inputBuffers[bufferIndex], bufferSize); if (len == AudioRecord.ERROR_INVALID_OPERATION || len == AudioRecord.ERROR_BAD_VALUE) { L.e("An error occured with the AudioRecord API !"); } else { //Log.v(TAG,"Pushing raw audio to the decoder: len="+len+" bs: "+inputBuffers[bufferIndex].capacity()); mMediaCodec.queueInputBuffer(bufferIndex, 0, len, System.nanoTime()/1000, 0); } } } } catch (RuntimeException e) { e.printStackTrace(); } } }); mThread.start(); // 设置 packetizer 的发送地址 mPacketizer.setDestination(mDestination, mRtpPort, mRtcpPort); // 设置 packetizer 的接收流,使其能够打包发送编码后的数据 mPacketizer.setInputStream(inputStream); // 开始循环获取inputStream的数据 mPacketizer.start(); mStreaming = true; } @Override public void stop() { if (mStreaming) { mThread.interrupt(); mAudioRecord.stop(); mAudioRecord.release(); mAudioRecord = null; super.stop(); } } @Override public String getSessionDescription() throws IllegalStateException { if (mSessionDescription == null) throw new IllegalStateException("You need to call configure() first !"); return mSessionDescription; } @Override public boolean isStreaming() { return mStreaming; } }