安卓MediaRecorder(3)音频采集编码写入详细源码分析

本文深入分析了MediaRecorder音频采集、编码、写入文件的详细流程。介绍了音频初始化、AudioRecord分析及AudioSource采集音频的过程,阐述了音频编码及编码后数据的处理方式,还说明了MPEG4Writer写入音频编码后数据到文件和取编码后音频数据的流程。

本文首发地址 https://h89.cn/archives/116.html

前言

通过 文2,我们知道 MediaRecorder 相关接口是在 StagefrightRecorder.cpp 中实现,本文进一步分析音频采集、编码、写入文件详细流程。

音频采集

音频初始化

通过前文,我们知道 setupAudioEncoder 在 setupMPEG4orWEBMRecording 中初始化,相关源码如下

// frameworks/av/media/libmediaplayerservice/StagefrightRecorder.cpp
status_t StagefrightRecorder::setupAudioEncoder() {
   
   
    sp<MediaCodecSource> audioEncoder = createAudioSource();
    return OK;
}
sp<MediaCodecSource> StagefrightRecorder::createAudioSource() {
   
   
    ...
    // 通过 AVFactory 工厂创建 AudioSource,并初始化 
    sp<AudioSource> audioSource = AVFactory::get()->createAudioSource(
                &attr,
                mAttributionSource,
                sourceSampleRate,
                mAudioChannels,
                mSampleRate,
                mSelectedDeviceId,
                mSelectedMicDirection,
                mSelectedMicFieldDimension);

}

那 AudioSource 是如何初始化的呢

// frameworks/av/media/libstagefright/AudioSource.cpp
void AudioSource::set(const audio_attributes_t *attr, const AttributionSourceState& attributionSource,
        uint32_t sampleRate, uint32_t channelCount, uint32_t outSampleRate,
        audio_port_handle_t selectedDeviceId,
        audio_microphone_direction_t selectedMicDirection,
        float selectedMicFieldDimension)
{
   
   
    ...
    // 构造了 一个 AudioRecord cpp 对象  
    mRecord = new AudioRecord(
        AUDIO_SOURCE_DEFAULT, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
        audio_channel_in_mask_from_count(channelCount),
        attributionSource,
        (size_t) (bufCount * frameCount),
        // 采集的音频数据回调 
        wp<AudioRecord::IAudioRecordCallback>{
   
   this},
        frameCount /*notificationFrames*/,
        AUDIO_SESSION_ALLOCATE,
        AudioRecord::TRANSFER_DEFAULT,
        AUDIO_INPUT_FLAG_NONE,
        attr,
        selectedDeviceId,
        selectedMicDirection,
        selectedMicFieldDimension);
   ...
}

AudioRecord.java 底层的实现也是 AudioSource.cpp
AudioRecord 主要是负责从麦克风设备采集音频 PCM 帧

AudioRecord 分析

// frameworks/av/media/libaudioclient/AudioRecord.cpp
status_t AudioRecord::set(...) {
   
   
    ...
    if (mCallback != nullptr) {
   
   
        // 启动录制的线程 
        mAudioRecordThread = new AudioRecordThread(*this);
        mAudioRecordThread->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
    }
    ...
}
bool AudioRecord::AudioRecordThread::threadLoop() {
   
   
    ...
    nsecs_t ns =  mReceiver.processAudioBuffer();
    ...
}

nsecs_t AudioRecord::processAudioBuffer() {
   
   
    ... 
    // 回调 AudioRecord::IAudioRecordCallback 
    if (newOverrun) {
   
   
        callback->onOverrun();

    }
    if (markerReached) {
   
   
        callback->onMarker(markerPosition.value());
    }
    while (newPosCount > 0) {
   
   
        callback->onNewPos(newPosition.value());
        newPosition += updatePeriod;
        newPosCount--;
    }
    if (mObservedSequence != sequence) {
   
   
        mObservedSequence = sequence;
        callback->onNewIAudioRecord();
    }

    while (mRemainingFrames > 0) {
   
   
        // 获取 audioBuffer 
        status_t err = obtainBuffer(&audioBuffer, requested, NULL, &nonContig);
        // 回调 取到的 buffer 到 AudioSource 中 onMoreData  
        const size_t readSize = callback->onMoreData(*buffer);
        // 释放 buffer 
        releaseBuffer(&audioBuffer);
    }
}

AudioSource 采集到音频

// frameworks/av/media/libstagefright/AudioSource.cpp
size_t AudioSource::onMoreData(const AudioRecord::Buffer& audioBuffer) {
   
    
    ...
    // 将AudioRecord::Buffer 放入 MediaBuffer
    MediaBuffer *buffer = new MediaBuffer(audioBuffer.size());
    memcpy((uint8_t *) buffer->data(),
            audioBuffer.data(), audioBuffer.size());
    buffer->set_range(0, audioBuffer.size());
    // 将 buffer 放入缓存
    queueInputBuffer_l(buffer, timeUs);
    return audioBuffer.size();
}
void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) {
   
   
    ...
    // 将 buffer 放入缓存 mBuffersReceived 中
    mBuffersReceived.push_back(buffer);
    mFrameAvailableCondition.signal();
}

// 如下接口可以读取采集到的 buffer
status_t 
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清霜辰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值