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