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
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()
|
|
}
|
|
|
|
} |