音频的基础知识
我们都知道音频是指通过声波传播的声音信号,我们要把声音的数据采集下来,进行传输或保存起来,就需要对声音进行编码。
在编码之前,先要对声音进行采集,采集就涉及到以下一些要素:
- 采样率(Sample Rate):采样率表示每秒钟对声音信号进行采样的次数,单位为赫兹(Hz)。常见的采样率有 44.1 kHz(CD质量)、48 kHz(视频、音频制作)、96 kHz(高保真音频)等。
- 位深度(Bit Depth):位深度表示每个采样点的样本精度,即用多少位来表示一个采样点的数值。常见的位深度有 16 位、24 位、32 位等。
- 声道:
- 单声道(Mono):只包含一个声道的音频信号。
- 立体声(Stereo):包含两个声道,通过左右声道的不同分配实现立体感。
- 多声道(Multi-channel):包含多个声道,例如 5.1 声道、7.1 声道等,常用于影院和环绕音效。
采集完后,得到的音频数据是PCM数据,可以通过MediaCodec 进行编码得到AAC数据,这样编码就完成了,我们可以把编码后的音频数据通过MediaMuxer 写入MP4 文件中,这样就可以播放音频了。
Android 中如何实现音频编码
- 使用 Android 给我们提供的 AudioRecord来完成音频的录制;
kotlin
private val audioRecord:AudioRecord by lazy {
AudioRecord(audioSource, sampleRateInHz, channelConfig, audioFormat, bufferSizeInBytes)
}
这样就创建了AudioRecord对象,里面的具体参数在下面的API有详细说明。 2. 创建编码器
kotlin
private val mediaCodec: MediaCodec by lazy {
val mimeType = MediaFormat.MIMETYPE_AUDIO_AAC // 音频编码格式
val mediaCodec = MediaCodec.createEncoderByType(mimeType)
val format = MediaFormat.createAudioFormat(mimeType, sampleRateInHz, 2)// 是双声道的声道数是2
format.setInteger(MediaFormat.KEY_BIT_RATE, audioBitRate)// 设置码率
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC)// AAC 编码的配置文件
format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, bufferSizeInBytes)
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
mediaCodec
}
- 创建MediaMuxer对象,用于MP4文件的写入
kotlin
private val mediaMuxer: MediaMuxer by lazy {
try {
val mediaMuxer = MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
mediaMuxer.setOrientationHint(90)
mediaMuxer
} catch (e: IOException) {
throw java.lang.RuntimeException(e)
}
}
- 开启AudioRecord、MediaCodec、MediaMuxer 在线程中读书数据进行编码,然后写入到MP4文件中
scss
fun start(){
nanoTime = System.nanoTime()
// 开启编码
mediaCodec.start()
// 开始录音
audioRecord.startRecording()
startEncodeThread()
}
- 开启一个线程读取数据
scss
private fun startEncodeThread() {
isRecord = true
Thread {
while (isRecord){
encodeAudio()
}
audioRecord.stop()
audioRecord.release()
mediaCodec.stop()
mediaCodec.release()
mediaMuxer.stop()
mediaMuxer.release()
}.start()
}
- 真正的读取数据
ini
private fun encodeAudio() {
bufferIndex = mediaCodec.dequeueInputBuffer(waitTime)
if (bufferIndex >= 0) {
val byteBuffer: ByteBuffer = mediaCodec.getInputBuffer(bufferIndex)?:throw Exception("byteBuffer is empty")
byteBuffer.clear()
len = audioRecord.read(byteBuffer, bufferSizeInBytes)
val presentationTimeUs: Long = ( System.nanoTime() - nanoTime) / 1000
if (len == AudioRecord.ERROR_INVALID_OPERATION
|| len == AudioRecord.ERROR_BAD_VALUE
) {
Log.e(
"TAG",
"An error occured with the AudioRecord API !"
)
} else {
mediaCodec.queueInputBuffer(bufferIndex, 0, len, presentationTimeUs, 0)
}
}
val videoBufferInfo = MediaCodec.BufferInfo()
var videobufferindex: Int = mediaCodec.dequeueOutputBuffer(videoBufferInfo, waitTime)
if (videobufferindex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
//添加轨道
audioTrackIndex = mediaMuxer.addTrack(mediaCodec.outputFormat)
mediaMuxer.start()
} else {
while (videobufferindex >= 0 && isRecord) {
//获取输出数据成功
val videoOutputBuffer: ByteBuffer =
mediaCodec.getOutputBuffer(videobufferindex)!!
mediaMuxer.writeSampleData(audioTrackIndex, videoOutputBuffer, videoBufferInfo)
mediaCodec.releaseOutputBuffer(videobufferindex, false)
videobufferindex = mediaCodec.dequeueOutputBuffer(videoBufferInfo, 0)
}
}
}
- 停止编码,释放资源
scss
fun stop(){
isRecord = false
}
Thread {
while (isRecord){
encodeAudio()
}
audioRecord.stop()
audioRecord.release()
mediaCodec.stop()
mediaCodec.release()
mediaMuxer.stop()
mediaMuxer.release()
}.start()
整体下来还是挺简单的,不过要注意的是,采样率、声道配置、音频数据格式 这些一定要设置好,后面解码的时候也要设置一样的,这样才可以正常播放。
用到的一些API
AudioRecord
java
public AudioRecord(int audioSource, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes)
-
audioSource - 音频源 这个参数表示从哪里获取音频数据。例如,使用麦克风作为音频源,可以传入 MediaRecorder.AudioSource.MIC 常量。除了麦克风之外,还可以使用其他音频源,例如电话输出、语音识别等。具体可参考MediaRecorder类中的定义。
-
sampleRateInHz - 采样率 这个参数指定录制音频数据所采取的样本频率,即每秒钟采集多少次音频数据。通常情况下,CD质量的音频采样率是44100Hz,而电话质量的音频采样率是8000Hz。
-
channelConfig - 声道配置 这个参数用于设置音频数据的声道数目。如果是单声道,则传入 AudioFormat.CHANNEL_IN_MONO 常量;如果是立体声,则传入 AudioFormat.CHANNEL_IN_STEREO 常量。
-
audioFormat - 音频数据格式 这个参数指定音频数据的存储格式。通常情况下,使用16位的PCM编码(即 AudioFormat.ENCODING_PCM_16BIT)即可满足需求。除此之外,还有一些压缩格式,例如 AMR和 AAC等。
-
bufferSizeInBytes - 缓存大小 这个参数指定内部音频缓存区的大小,以字节为单位。具体大小需要根据采样率、声道数以及编码格式来计算。
编码器中 MediaFormat.KEY_AAC_PROFILE
的设置
KEY_AAC_PROFILE
的值代表了 AAC 编码的配置文件(Profile)。以下是一些常见的 KEY_AAC_PROFILE
对应的值:
MediaCodecInfo.CodecProfileLevel.AACObjectMain
:主配置文件(Main Profile)。MediaCodecInfo.CodecProfileLevel.AACObjectLC
:低复杂度配置文件(Low Complexity Profile),也是最常用的配置文件。MediaCodecInfo.CodecProfileLevel.AACObjectSSR
:可扩展采样率(Scalable Sample Rate)配置文件。MediaCodecInfo.CodecProfileLevel.AACObjectLTP
:长时预测(Long Term Prediction)配置文件。MediaCodecInfo.CodecProfileLevel.AACObjectHE
:高效率配置文件(High Efficiency Profile)。MediaCodecInfo.CodecProfileLevel.AACObjectELD
:Enhanced Low Delay 配置文件。
这里列出的是一些常见的 AAC 音频编码配置文件,但具体支持的配置文件取决于设备和系统中可用的 AAC 编码器。在使用 MediaFormat.KEY_AAC_PROFILE
参数时,需要根据目标平台和目标设备的支持情况来选择合适的配置文件。
应注意,不同的配置文件可能支持不同的功能和性能特性,例如低延迟、高效率或更高音质,因此在选择配置文件时需根据实际需求进行权衡和选择。
总结
- 今天就简单介绍了音频编码的实现,当然也可以使用FFMpeg来实现音视频的编解码,这部分的内容放到后面再做介绍了