音频解码

上一篇文章介绍了音频编码,接着我们来看看音频解码是如何实现的,在解码后再播放出音频。

音频解码

  1. 创建一个用于播放音频的 AudioTrack,AudioTrack用于播放PCM数据的。
scss 复制代码
val audioTrack = AudioTrack.Builder()
    .setAudioAttributes(AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_MEDIA)//用于设置音频属性中的用途,AudioAttributes.USAGE_MEDIA: 媒体播放。用于普通的音乐播放、视频播放等。
        .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)//用于设置音频属性中的内容类型,
            //AudioAttributes.CONTENT_TYPE_MUSIC: 音乐内容。用于播放音乐、唱片、电台广播等音乐场景。
        .build())
    .setAudioFormat(AudioFormat.Builder()
        .setEncoding(audioFormat)//用于设置音频格式中的编码方式
        .setSampleRate(sampleRateInHz)
        .setChannelMask(channelConfig)
        .build())
    .setBufferSizeInBytes(bufferSizeInBytes)//设置缓存区的大小
    .build()
  1. 创建一个 解复用的 MediaExtractor
scss 复制代码
val extractor = MediaExtractor()
extractor.setDataSource(inputPath)
  1. 找到音频轨数据,并选择音频轨数据
ini 复制代码
val extractorAudioIndex = extractor.getTrackIndex(MediaType.AUDIO)
if (extractorAudioIndex == -1) return
extractor.selectTrack(extractorAudioIndex)
kotlin 复制代码
 fun MediaExtractor.getTrackIndex( type: MediaType): Int {

    val numTracks = this.trackCount
    for (i in 0 until numTracks) {
        val format = this.getTrackFormat(i)
        val mime = format.getString(MediaFormat.KEY_MIME)
        if (mime?.startsWith(if (type == MediaType.AUDIO) "audio/" else "video/", false) == true) {
            return i
        }
    }

    return -1
}
  1. 从 MediaExtractor拿到音频格式,然后创建解码器
java 复制代码
val audioFormat = extractor.getTrackFormat(extractorAudioIndex)
// 创建解码器

    val mediaCodec =
        MediaCodec.createDecoderByType(audioFormat.getString(MediaFormat.KEY_MIME) ?: "")
    mediaCodec.configure(audioFormat, null, null, 0)

5.开启 AudioTrack 和 MediaCodec

scss 复制代码
mediaCodec.start()
audioTrack.play()
  1. 从 MediaExtractor 读取数据,然后写入到 AudioTrack 就可以进行播放了
kotlin 复制代码
var isInputFinished = false
var inputBufferIndex: Int
var outputBufferIndex: Int
val bufferInfo = MediaCodec.BufferInfo()
while (isPlay) {
    if (!isInputFinished) {
        // 开始解码
        // 获取输入索引号
        inputBufferIndex = mediaCodec.dequeueInputBuffer(1000)
        if (inputBufferIndex >= 0) {
            // 获取输入buffer
            val inputBuffer = mediaCodec.getInputBuffer(inputBufferIndex)
                ?: throw RuntimeException("MediaCodec.getInputBuffer returned null")
            // 添加数据到buffer 中
            val sampleSize = extractor.readSampleData(inputBuffer, 0)
            if (sampleSize >= 0) {
                // 放到解码器
                mediaCodec.queueInputBuffer(
                    inputBufferIndex, 0, sampleSize, extractor.sampleTime, 0
                )
                extractor.advance()
            } else {
                isInputFinished = true
                mediaCodec.queueInputBuffer(
                    inputBufferIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM
                )
            }
        }
        // 拿到输出索引号
        outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 1000)

        if (outputBufferIndex >= 0) {
            // 获取输出buffer
            val outputBuffer =
                mediaCodec.getOutputBuffer(outputBufferIndex) ?: throw RuntimeException(
                    "MediaCodec.getOutputBuffer returned null"
                )

            val pcmData = ByteArray(bufferInfo.size)
            outputBuffer.get(pcmData)
            audioTrack.write(pcmData,0,bufferInfo.size)
            mediaCodec.releaseOutputBuffer(outputBufferIndex, false)
            if ((bufferInfo.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
                "getPCMDataFromMP4 end BUFFER_FLAG_END_OF_STREAM".log()
                break
            }

        } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
            // 格式发生变化,可在此处进行相关处理
        }
    }
}
"getPCMDataFromMP4 end---".log()
mediaCodec.stop()
mediaCodec.release()

用到的一些API

音频的解码到播放还是比较简单的,里面有几个API要说明一下:

AudioAttributes.setUsage

用于设置音频属性中的用途。在 Android 中,音频属性被用于描述音频流的特征和用途,以便系统能够做出相应的处理和优化。

这个方法接受一个整数参数,表示具体的音频用途。以下是一些常用的音频用途常量:

  • AudioAttributes.USAGE_UNKNOWN: 未知用途。
  • AudioAttributes.USAGE_MEDIA: 媒体播放。用于普通的音乐播放、视频播放等。
  • AudioAttributes.USAGE_VOICE_COMMUNICATION: 语音通信。用于电话、VoIP 通话等语音通信场景。
  • AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING: 语音通信信号。用于传输语音通信的信号音,如拨号音、呼叫等待音。
  • AudioAttributes.USAGE_ALARM: 闹钟。用于闹钟声音。
  • AudioAttributes.USAGE_NOTIFICATION: 通知。
  • AudioAttributes.USAGE_NOTIFICATION_RINGTONE: 通知铃声。用于通知的铃声。
  • AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_REQUEST: 通知通信请求。
  • AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_INSTANT: 即时通信通知。
  • AudioAttributes.USAGE_NOTIFICATION_COMMUNICATION_DELAYED: 延迟通信通知。
  • AudioAttributes.USAGE_NOTIFICATION_EVENT: 通知事件。
  • AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY: 辅助功能。
  • AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE: 导航指引音频。
  • AudioAttributes.USAGE_ASSISTANCE_SONIFICATION: 声音反馈。

可以根据具体的场景和需求选择适合的音频用途,以便系统能够对音频流做出合适的处理和优化。例如,如果正在开发一个音乐播放器应用,可以使用 AudioAttributes.USAGE_MEDIA 来设置音频属性的用途,以确保在播放音乐时系统能够做出相应优化,如提供更好的音频质量和音量控制。

AudioAttributes.setContentType

用于设置音频属性中的内容类型。在 Android 中,音频属性被用于描述音频流的特征和用途,以便系统能够做出相应的处理和优化。

这个方法接受一个整数参数,表示具体的音频内容类型。以下是一些常用的音频内容类型常量:

  • AudioAttributes.CONTENT_TYPE_UNKNOWN: 未知内容类型。
  • AudioAttributes.CONTENT_TYPE_SPEECH: 语音内容。用于识别和生成语音、通话等语音场景。
  • AudioAttributes.CONTENT_TYPE_MUSIC: 音乐内容。用于播放音乐、唱片、电台广播等音乐场景。
  • AudioAttributes.CONTENT_TYPE_MOVIE: 电影内容。用于播放电影、视频、电视节目等影视场景。
  • AudioAttributes.CONTENT_TYPE_SONIFICATION: 声音反馈内容。用于 UI 交互和操作提示等场景。

可以根据具体的场景和需求选择适合的音频内容类型,以便系统能够对音频流做出合适的处理和优化。例如,如果正在开发一个音乐播放器应用,可以使用 AudioAttributes.CONTENT_TYPE_MUSIC 来设置音频属性的内容类型,以确保在播放音乐时系统能够做出相应优化,如提供更好的音频质量和音量控制。

AudioFormat.setEncoding

用于设置音频格式中的编码方式。在 Android 中,音频格式被用于描述音频流的数据格式和参数,而编码方式则决定了如何将音频数据转换为数字信号以便处理和传输。

这个方法接受一个整数参数,表示具体的编码方式。以下是一些常用的编码方式常量:

  • AudioFormat.ENCODING_INVALID: 无效编码。用于未知或不支持的编码方式。
  • AudioFormat.ENCODING_DEFAULT: 默认编码。用于没有指定具体编码方式时使用系统默认编码。
  • AudioFormat.ENCODING_PCM_16BIT: 16 位 PCM 编码。一种常见的无损音频编码方式,用于存储原始音频数据。
  • AudioFormat.ENCODING_PCM_8BIT: 8 位 PCM 编码。另一种常见的无损音频编码方式,用于存储原始音频数据。
  • AudioFormat.ENCODING_AC3: AC-3 编码。一种常见的有损音频编码方式,用于压缩多通道音频数据。
  • AudioFormat.ENCODING_E_AC3: Enhanced AC-3 编码。AC-3 的升级版,支持更高质量的音频压缩和编解码。
  • AudioFormat.ENCODING_DTS: DTS 编码。一种常见的有损音频编码方式,用于压缩多通道音频数据。
  • AudioFormat.ENCODING_AC4: AC-4 编码。一种新型的有损音频编码方式,支持更高质量和更高效率的音频压缩和编解码。

可以根据具体的需求和兼容性要求选择适合的音频编码方式。例如,如果需要存储或传输无损音频数据,可以使用 AudioFormat.ENCODING_PCM_16BITAudioFormat.ENCODING_PCM_8BIT 来设置音频格式的编码方式,以确保音频数据能够被准确地存储和还原。如果你需要压缩多通道音频数据,你可以使用 AC-3、E-AC3、DTS 等有损编码方式,以便实现更高效的数据压缩和传输。

AudioFormat.setChannelMask

用于设置音频格式中的通道掩码。在 Android 中,通道掩码用于描述音频流中每个通道的配置和使用情况。

这个方法接受一个整数参数,表示具体的通道掩码。以下是一些常用的通道掩码常量:

  • AudioFormat.CHANNEL_OUT_MONO: 单声道。只包含一个声道的音频数据。
  • AudioFormat.CHANNEL_OUT_STEREO: 立体声。包含左右两个声道的音频数据。
  • AudioFormat.CHANNEL_OUT_5POINT1: 5.1 声道。包含前左、前右、中央、低音炮、后左和后右六个声道的音频数据。
  • AudioFormat.CHANNEL_OUT_7POINT1: 7.1 声道。包含前左、前右、中央、低音炮、后左、后右、顶部前中和顶部后中八个声道的音频数据。
  • AudioFormat.CHANNEL_IN_MONO: 单声道输入。只使用一个声道进行音频采集。
  • AudioFormat.CHANNEL_IN_STEREO: 立体声输入。使用左右两个声道进行音频采集。

可以根据具体的需求和音频配置来选择适合的通道掩码。例如,如果需要处理单声道音频数据,可以使用 AudioFormat.CHANNEL_OUT_MONOAudioFormat.CHANNEL_IN_MONO 来设置音频格式的通道掩码;如果你需要处理立体声音频数据,你可以使用 AudioFormat.CHANNEL_OUT_STEREOAudioFormat.CHANNEL_IN_STEREO

总结

  • 今天就简单的介绍了音频的解码,主要涉及到了几个类的使用:
  • AudioTrack:音频播放器,播放的数据是PCM数据;
  • MediaCodec: 解码器在解码音频的时候的使用;
  • MediaExtractor:音视频文件解封装,拿到音频数据,然后进行解码。
相关推荐
关键帧Keyframe15 小时前
音视频面试题集锦第 15 期 | 编辑 SDK 架构 | 直播回声 | 播放器架构
音视频开发·视频编码·客户端
关键帧Keyframe6 天前
iOS 不用 libyuv 也能高效实现 RGB/YUV 数据转换丨音视频工业实战
音视频开发·视频编码·客户端
关键帧Keyframe8 天前
音视频面试题集锦第 7 期
音视频开发·视频编码·客户端
关键帧Keyframe8 天前
音视频面试题集锦第 8 期
ios·音视频开发·客户端
蚝油菜花13 天前
MimicTalk:字节跳动和浙江大学联合推出 15 分钟生成 3D 说话人脸视频的生成模型
人工智能·开源·音视频开发
音视频牛哥15 天前
Android平台RTSP|RTMP播放器高效率如何回调YUV或RGB数据?
音视频开发·视频编码·直播
<Sunny>17 天前
MPP音视频总结
音视频开发·1024程序员节·海思mpp
GoFly开发者17 天前
GoFly快速开发框架已集成了RTSP流媒体服务器(直播、录播)代码插件-开发者集成流媒体服务器更加方便
go·音视频开发
音视频牛哥1 个月前
如何设计开发RTSP直播播放器?
音视频开发·视频编码·直播
dvlinker1 个月前
【音视频开发】使用支持硬件加速的D3D11绘图遇到的绘图失败与绘图崩溃问题的记录与总结
音视频开发·c/c++·视频播放·d3d11·d3d11绘图模式