【Android MediaCodec 将音频转换为 pcm 格式】

调用方法

	var url = "https://xxxx.mp3"
	DownloadAndConvertTask(context.cacheDir.path).execute(url)

代码实现


    class DownloadAndConvertTask constructor(cacheDirPath: String): AsyncTask<String?, Void?, Void?>() {
		//缓存路径
        var cacheDir = File(cacheDirPath)
        override fun doInBackground(vararg params: String?): Void? {
            if (params.isNotEmpty()) {
                val audioUrl = params[0]
                if (audioUrl != null) {
                    val path = downloadAudioFile(audioUrl)
                    val pcmPath = decodeAudioToPcm(path)
                    if(!pcmPath.isNullOrBlank()){
                        // TODO ... 这里拿到pcm文件路径
                        
                    }
                }
            }
            return null
        }

        /**
         * 下載音频文件到本地
         * @param audioUrl 要下载的音频链接
         * @return 下载到本地的文件路径
         */
        private fun downloadAudioFile(audioUrl: String): String{
            Log.d("downloadAndConvert", "audioUrl = "+audioUrl)
            var urlConnection: HttpURLConnection?
            var outputStream: FileOutputStream?

            // Open a connection to the audio file URL
            val url = URL(audioUrl)
            urlConnection = url.openConnection() as HttpURLConnection
            urlConnection.connect()

            // Create a temporary file to store the downloaded audio data
            val tempFile: File = File.createTempFile("temp_audio", null, cacheDir)

            outputStream = FileOutputStream(tempFile)

            // Write the downloaded audio data to the temporary file
            val buffer = ByteArray(1024)
            var bytesRead: Int
            while (urlConnection.inputStream.read(buffer).also { bytesRead = it } != -1) {
                outputStream.write(buffer, 0, bytesRead)
            }
            outputStream.close()
            urlConnection.inputStream.close()
            urlConnection.disconnect()

            return tempFile.absolutePath

        }

        /**
         * 音頻文件解码为pcm格式
         * @param inputAudioFile 要转码的音频文件路径
         * @return pcm文件路径
         */
        private fun decodeAudioToPcm(inputAudioFile: String): String? {
            Log.d(TAG, "decodeAudioToPcm start inputAudioFile = $inputAudioFile")

            if(inputAudioFile.isEmpty()){
                return null
            }
            if(inputAudioFile.endsWith(".pcm")){
                return inputAudioFile
            }
            val timeStart = System.currentTimeMillis()

            val mediaExtractor = MediaExtractor()
            var mediaCodec: MediaCodec? = null
            var outputStream: FileOutputStream? = null
            val tempPcmFile: File = File.createTempFile("temp_audio_pcm", ".pcm", cacheDir)
            try {
                // 设置数据源为输入音频文件
                mediaExtractor.setDataSource(inputAudioFile)

                // 选择第一个默认轨道(多个音轨时,请注意选择音轨)
                mediaExtractor.selectTrack(0)

                // 获取音频轨道的格式
                val inputFormat = mediaExtractor.getTrackFormat(0)

                Log.d(TAG, "decodeAudioToPcm MediaFormat.KEY_MIME = "+inputFormat.getString(MediaFormat.KEY_MIME))

                // 创建用于解码的 MediaCodec
                mediaCodec = MediaCodec.createDecoderByType(inputFormat.getString(MediaFormat.KEY_MIME)!!)
                mediaCodec.configure(inputFormat, null, null, 0)
                mediaCodec.start()

                // 开始解码并写入到 PCM 文件
                val codecInputBuffers = mediaCodec.inputBuffers
                val codecOutputBuffers = mediaCodec.outputBuffers
                val bufferInfo = MediaCodec.BufferInfo()

                // 输出 PCM 文件的路径
                val pcmOutputFile = File(tempPcmFile.absolutePath)
                outputStream = FileOutputStream(pcmOutputFile)

                var isDone = false
                while (!isDone) {
                    // 获取可用的输入缓冲区
                    val inputBufferIndex = mediaCodec.dequeueInputBuffer(10000)
                    if (inputBufferIndex >= 0) {
                        // 获取输入缓冲区
                        val inputBuffer = codecInputBuffers[inputBufferIndex]
                        // 读取音频数据到输入缓冲区
                        val sampleSize = mediaExtractor.readSampleData(inputBuffer, 0)
                        if (sampleSize < 0) {
                            // 如果没有更多数据,发送 EOS(End of Stream)标志
                            mediaCodec.queueInputBuffer(inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM)
                            isDone = true
                        } else {
                            // 将数据放入输入缓冲区,并前进到下一帧
                            mediaCodec.queueInputBuffer(inputBufferIndex, 0, sampleSize, mediaExtractor.sampleTime, 0 )
                            mediaExtractor.advance()
                        }
                    }

                    // 获取解码后的输出数据
                    val outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000)
                    if (outputBufferIndex >= 0) {
                        // 获取输出缓冲区
                        val outputBuffer = codecOutputBuffers[outputBufferIndex]
                        // 将 PCM 数据从输出缓冲区中读取到字节数组中
                        val chunk = ByteArray(bufferInfo.size)
                        outputBuffer.get(chunk)
                        outputBuffer.clear()

                        // 将 PCM 数据写入到输出文件
                        outputStream.write(chunk, 0, chunk.size)

                        // 释放输出缓冲区
                        mediaCodec.releaseOutputBuffer(outputBufferIndex, false)
                    }
                }
            } catch (e: IOException) {
                Log.e(TAG, "Error decoding audio to PCM: " + e.message)
                return null
            } finally {
                try {
                    // 关闭资源
                    if (mediaCodec != null) {
                        mediaCodec.stop()
                        mediaCodec.release()
                    }
                    mediaExtractor.release()
                    outputStream?.close()
                } catch (e: IOException) {
                    e.printStackTrace()
                }
            }

            Log.d(TAG, "decodeAudioToPcm end outputPcmFile = "+tempPcmFile.absolutePath)
            Log.d(TAG, "decodeAudioToPcm end outputPcmFile.length = "+tempPcmFile.length())
            Log.d(TAG, "decodeAudioToPcm time consuming (ms) = "+(System.currentTimeMillis()-timeStart))

            return tempPcmFile.absolutePath
        }
    }

你可能感兴趣的:(Android,android,音视频,pcm,MediaCodec)