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