You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

146 lines
5.1 KiB

package com.frank.androidmedia.controller
import android.media.*
import android.os.SystemClock
import android.util.Log
import java.lang.Exception
/**
* 1. Using MediaExtractor to demux audio.
* 2. Using MediaCodec to decode audio.
* 3. Using AudioTrack to play audio.
* See also AAduio, oboe, openSL ES.
*
* @author frank
* @date 2022/3/21
*/
open class AudioTrackController {
companion object {
val TAG: String = AudioTrackController::class.java.simpleName
private const val DEQUEUE_TIME = (10 * 1000).toLong()
private const val SLEEP_TIME: Long = 20
}
private var audioTrack: AudioTrack? = null
private var mediaCodec: MediaCodec? = null
private var mediaExtractor: MediaExtractor? = null
private fun parseAudioFormat(path: String): MediaFormat? {
mediaExtractor = MediaExtractor()
try {
mediaExtractor?.setDataSource(path)
for (i in 0 until mediaExtractor!!.trackCount) {
val mediaFormat = mediaExtractor!!.getTrackFormat(i)
val mimeType = mediaFormat.getString(MediaFormat.KEY_MIME)
if (mimeType != null && mimeType.startsWith("audio")) {
mediaExtractor!!.selectTrack(i)
return mediaFormat
}
}
} catch (e: Exception) {
Log.e(TAG, "parseAudioFormat err=$e")
}
return null
}
private fun initMediaCodec(mediaFormat: MediaFormat): Boolean {
val mimeType = mediaFormat.getString(MediaFormat.KEY_MIME)
mediaCodec = MediaCodec.createDecoderByType(mimeType)
return try {
mediaCodec!!.configure(mediaFormat, null, null, 0)
mediaCodec!!.start()
true
} catch (e: Exception) {
Log.e(TAG, "initMediaCodec err=$e")
false
}
}
private fun initAudioTrack(mediaFormat: MediaFormat): Boolean {
val sampleRate = mediaFormat.getInteger(MediaFormat.KEY_SAMPLE_RATE)
val channelCount = mediaFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT)
val channelConfig = if (channelCount == 1) {
AudioFormat.CHANNEL_OUT_MONO
} else {
AudioFormat.CHANNEL_OUT_STEREO
}
val audioFormat = AudioFormat.ENCODING_PCM_16BIT
val bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat)
Log.e(TAG, "sampleRate=$sampleRate, channelCount=$channelCount, bufferSize=$bufferSize")
try {
audioTrack = AudioTrack(AudioManager.STREAM_MUSIC, sampleRate, channelConfig, audioFormat, bufferSize, AudioTrack.MODE_STREAM)
audioTrack!!.play()
// audioTrack!!.attachAuxEffect()
} catch (e: Exception) {
Log.e(TAG, "initAudioTrack err=$e")
return false
}
return true
}
private fun release() {
if (mediaExtractor != null) {
mediaExtractor!!.release()
mediaExtractor = null
}
if (mediaCodec != null) {
mediaCodec!!.release()
mediaCodec = null
}
if (audioTrack != null) {
audioTrack!!.release()
audioTrack = null
}
}
fun playAudio(path: String) {
val data = ByteArray(10 * 1024)
var finished = false
val bufferInfo = MediaCodec.BufferInfo()
val mediaFormat = parseAudioFormat(path) ?: return release()
var result = initMediaCodec(mediaFormat)
if (!result) {
return release()
}
result = initAudioTrack(mediaFormat)
if (!result) {
return release()
}
while (!finished) {
val inputIndex = mediaCodec!!.dequeueInputBuffer(DEQUEUE_TIME)
if (inputIndex >= 0) {
val inputBuffer = mediaCodec!!.getInputBuffer(inputIndex)
// demux
val sampleSize = mediaExtractor!!.readSampleData(inputBuffer!!, 0)
Log.e(TAG, "sampleSize=$sampleSize")
// decode
if (sampleSize < 0) {
mediaCodec!!.queueInputBuffer(inputIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
finished = true
} else {
mediaCodec!!.queueInputBuffer(inputIndex, 0, sampleSize, mediaExtractor!!.sampleTime, mediaExtractor!!.sampleFlags)
mediaExtractor!!.advance()
}
}
val outputIndex = mediaCodec!!.dequeueOutputBuffer(bufferInfo, DEQUEUE_TIME)
// play
if (outputIndex >= 0) {
val outputBuffer = mediaCodec!!.getOutputBuffer(outputIndex)
val size = outputBuffer!!.limit()
Log.e(TAG, "outputIndex=$outputIndex, position=" + outputBuffer.position() + " ,limit=" + size)
outputBuffer.get(data, outputBuffer.position(), size)
audioTrack!!.write(data, 0, size)
mediaCodec!!.releaseOutputBuffer(outputIndex, false)
SystemClock.sleep(SLEEP_TIME)
}
}
release()
}
}