parent
a1276b776e
commit
60611bcda6
@ -0,0 +1,146 @@ |
||||
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() |
||||
} |
||||
|
||||
} |
Loading…
Reference in new issue