咱们在发送端向端口40020发送数据,因此这里监听的是40020端口,payload type是97与发送端设置的一致。git
audioRtpWrapper.open(40020, 97, 1000);
设置rtp callback用于接收aac原始数据。github
audioRtpWrapper.setCallback { data, len -> if (len < 4) return@setCallback val index = indexArray.take() if (currentTime == 0L) { currentTime = System.currentTimeMillis() } val buffer = audioDecodeCodec.mediaCodec.getInputBuffer(index) val time = (System.currentTimeMillis() - currentTime) * 1000 if (hasAuHeader) { buffer?.position(0) buffer?.put(data, 4, len - 4); buffer?.position(0) audioDecodeCodec.mediaCodec.queueInputBuffer(index, 0, len - 4, time, 1) } else { buffer?.position(0) buffer?.put(data, 0, len); buffer?.position(0) audioDecodeCodec.mediaCodec.queueInputBuffer(index, 0, len, time, 1) } };
接收到aac数据后直接写入MediaCodec的inputbuffer,这里针对是否有au header来处理数据。若是有au header,那么跳过au header数据。au header length 和 au header共占用4 bytes。在经过index获取inputbuffer的时候要格外注意index的有效性,即index必须是mediacodec释放出来的buffer的index,不然会发送buffer写入错误。segmentfault
建立MediaCodec的时候要指定采样率、通道数、格式等信息,这些信息须要与发送端保持一致。app
val sampleRate = 44100 val audioFormat = MediaFormat.createAudioFormat( MediaFormat.MIMETYPE_AUDIO_AAC, sampleRate, audioChannelCount ) audioFormat.setByteBuffer("csd-0", audioSpecificConfig) var currentTime = 0L indexArray.clear() audioDecodeCodec = object : AudioDecodeCodec(audioFormat) { override fun onInputBufferAvailable(codec: MediaCodec, index: Int) { indexArray.put(index) } }
这里的"csd-0"参数要格外的关注,由于这个参数不设置或是设置错误都会在解码过程当中发生buffer的读写错误。audioSpecificConfig这个变量的生成规则能够参考下面这段代码:ide
private val audioChannelCount = 1; private val audioProfile = 1 /** * 97000, 88200, 64000, 48000,44100, 32000, 24000, 22050,16000, 12000, 11025, 8000,7350, 0, 0, 0 */ private val audioIndex = 4 private val audioSpecificConfig = ByteArray(2).apply { this[0] = ((audioProfile + 1).shl(3).and(0xff)).or(audioIndex.ushr(1).and(0xff)).toByte() this[1] = ((audioIndex.shl(7).and(0xff)).or(audioChannelCount.shl(3).and(0xff))).toByte() }.let { val buffer = ByteBuffer.allocate(2) buffer.put(it) buffer.position(0) buffer }
固然若是不设置csd-0这个参数,那么从rtp返回的数据中有config数据也是能够的。可是rtp是广播形式的,它并不知道何时须要广播config数据,因此配置csd-0是比较好的选择。(注意:config数据指的是发送端的MediaCodec编码启动后生成的config 数据buffer)this
首先根据mediaformat信息建立AudioTrack并进行播放,这里设置的参数要与MediaCodec设置的一致,同事这种流模式播放(MODE_STREAM)编码
override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) { val sampleRate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE) val channelCount = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT) val minBufferSize = AudioRecord.getMinBufferSize(sampleRate, if (channelCount == 1) AudioFormat.CHANNEL_IN_MONO else AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT); audioTrack = AudioTrack(AudioManager.STREAM_VOICE_CALL, sampleRate, channelCount, AudioFormat.ENCODING_PCM_16BIT, minBufferSize, MODE_STREAM); audioTrack?.play(); }
从MediaCodec读取解码后的数据并写入AudioTrack,这里采用的是block方式写入。spa
override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) { Log.d("audio_dragon","onOutputBufferAvailable $index") kotlin.runCatching { val buffer = codec.getOutputBuffer(index) ?: return; buffer.position(info.offset); audioTrack?.write(buffer, info.size, WRITE_BLOCKING); codec.releaseOutputBuffer(index, false); } }