AudioTrack的声音输出流程

这篇具有很好参考价值的文章主要介绍了AudioTrack的声音输出流程。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

通过普通AudioTrack的流程追踪数据流。分析一下声音模块的具体流程。这里比较复杂的是binder以及共享内存。这里不做详细介绍。只介绍原理

正文

java层的AudioTrack主要是通过jni调用到cpp层的AudioTrack。我们只介绍cpp层相关。

初始化

初始化只核心是通过set函数实现的。主要包括三步。

1. 客户端准备数据,
status_t AudioTrack::set(
        audio_stream_type_t streamType,
        uint32_t sampleRate,
        audio_format_t format,
        audio_channel_mask_t channelMask,
        size_t frameCount,
        audio_output_flags_t flags,
        callback_t cbf,
        void* user,
        int32_t notificationFrames,
        const sp<IMemory>& sharedBuffer,
        bool threadCanCallJava,
        audio_session_t sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        uid_t uid,
        pid_t pid,
        const audio_attributes_t* pAttributes,
        bool doNotReconnect,
        float maxRequiredSpeed,
        audio_port_handle_t selectedDeviceId)
{
   //如果构造audioTrack时时传入AudioTrack.MODE_STREAM。则sharedBuffer为空
    mSharedBuffer = sharedBuffer;

    if (cbf != NULL) {
        mAudioTrackThread = new AudioTrackThread(*this);
        mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
        // thread begins in paused state, and will not reference us until start()
    }

    // create the IAudioTrack
    {
        AutoMutex lock(mLock);
        //这是核心,通过audiofligure 创建服务端数据,以及拿到共享内存。
        status = createTrack_l();
    }
    mVolumeHandler = new media::VolumeHandler();
}


status_t AudioTrack::createTrack_l()
{
    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger()
    IAudioFlinger::CreateTrackInput inpu
    IAudioFlinger::CreateTrackOutput output;
//关键部分
    sp<IAudioTrack> track = audioFlinger->createTrack(input,output,&status);

    sp<IMemory> iMem = track->getCblk();

    void *iMemPointer = iMem->pointer();

    mAudioTrack = track;
    mCblkMemory = iMem;

    audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
    mCblk = cblk;

    void* buffers;
        buffers = cblk + 1;

    mAudioTrack->attachAuxEffect(mAuxEffectId);

    if (mFrameCount > mReqFrameCount) {
        mReqFrameCount = mFrameCount;
    }

    // update proxy
    if (mSharedBuffer == 0) {
        mStaticProxy.clear();
        mProxy = new AudioTrackClientProxy(cblk, buffers, mFrameCount, mFrameSize);
    }
    
    mProxy->setPlaybackRate(playbackRateTemp);
    mProxy->setMinimum(mNotificationFramesAct);
    }
}

核心是createTrack,之后拿到关键的共享内存消息,然后写入内容

2. Audiofligure创建远端track。

sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
                                          CreateTrackOutput& output,
                                          status_t *status)
{
    sp<PlaybackThread::Track> track;
    sp<TrackHandle> trackHandle;
    sp<Client> client;
    
    pid_t clientPid = input.clientInfo.clientPid;
    const pid_t callingPid = IPCThreadState::self()->getCallingPid();
    


    {
        Mutex::Autolock _l(mLock);
        PlaybackThread *thread = checkPlaybackThread_l(output.outputId);


        client = registerPid(clientPid);

        PlaybackThread *effectThread = NULL;

        track = thread->createTrack_l(client, streamType, localAttr, &output.sampleRate,
                                      input.config.format, input.config.channel_mask,
                                      &output.frameCount, &output.notificationFrameCount,
                                      input.notificationsPerBuffer, input.speed,
                                      input.sharedBuffer, sessionId, &output.flags,
                                      callingPid, input.clientInfo.clientTid, clientUid,
                                      &lStatus, portId);
        
    trackHandle = new TrackHandle(track);

    return trackHandle;
}

核心是createTrack_l,在混音线程中加入新建一个服务端的Track。新建共享内存,返还给客户端的Track,最终可以共享数据。代码如下:


// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
        const sp<AudioFlinger::Client>& client,
        const sp<IMemory>& sharedBuffer,)
{
    sp<Track> track;

        track = new Track(this, client, streamType, attr, sampleRate, format,
                          channelMask, frameCount,
                          nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
                          sessionId, creatorPid, uid, *flags, TrackBase::TYPE_DEFAULT, portId);
			//这个不太重要,后面调用start后,track会进入mActiveTracks列表,最终会在Threadloop中读取数据,经过处理写入out中
        mTracks.add(track);
        
    return track;
}

Track的构造函数是关键,主要是拿到共享内存。交给binder服务端的TrackHandle对象。大概代码如下:

AudioFlinger::ThreadBase::TrackBase::TrackBase(
            ThreadBase *thread,
            const sp<Client>& client
            void *buffer,)
    :   RefBase(),
{
   //client是audiofligure中维护的一个变量,主要持有MemoryDealer。用于管理共享内存。
    if (client != 0) {
        mCblkMemory = client->heap()->allocate(size);
        
    }
}

然后把tracxk对象封装成TrackHandle这个binder对象,传给AudioTrack,通过TrackHandle实现数据传输。他主要实现了start和stop。以及获取共享内存的接口如下


class IAudioTrack : public IInterface
{
public:
    DECLARE_META_INTERFACE(AudioTrack);

    /* Get this track's control block */
    virtual sp<IMemory> getCblk() const = 0;

    /* After it's created the track is not active. Call start() to
     * make it active.
     */
    virtual status_t    start() = 0;

    /* Stop a track. If set, the callback will cease being called and
     * obtainBuffer will return an error. Buffers that are already released
     * will continue to be processed, unless/until flush() is called.
     */
    virtual void        stop() = 0;

    /* Flush a stopped or paused track. All pending/released buffers are discarded.
     * This function has no effect if the track is not stopped or paused.
     */
    virtual void        flush() = 0;

    /* Pause a track. If set, the callback will cease being called and
     * obtainBuffer will return an error. Buffers that are already released
     * will continue to be processed, unless/until flush() is called.
     */
    virtual void        pause() = 0;

    /* Attach track auxiliary output to specified effect. Use effectId = 0
     * to detach track from effect.
     */
    virtual status_t    attachAuxEffect(int effectId) = 0;

    /* Send parameters to the audio hardware */
    virtual status_t    setParameters(const String8& keyValuePairs) = 0;

    /* Selects the presentation (if available) */
    virtual status_t    selectPresentation(int presentationId, int programId) = 0;

    /* Return NO_ERROR if timestamp is valid.  timestamp is undefined otherwise. */
    virtual status_t    getTimestamp(AudioTimestamp& timestamp) = 0;

    /* Signal the playback thread for a change in control block */
    virtual void        signal() = 0;

    /* Sets the volume shaper */
    virtual media::VolumeShaper::Status applyVolumeShaper(
            const sp<media::VolumeShaper::Configuration>& configuration,
            const sp<media::VolumeShaper::Operation>& operation) = 0;

    /* gets the volume shaper state */
    virtual sp<media::VolumeShaper::State> getVolumeShaperState(int id) = 0;
};

下面大概介绍一下写入数据的过程,因为这个算法比较复杂,主要是通过共享内存,通过共享内存中的结构体,控制共享内存中后半部分的数据写入写出。就不在详细介绍。这里只简要介绍大概流程:
首先是Audiotrack的write方法:

ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)

//申请空间,
        status_t err = obtainBuffer(&audioBuffer,
                blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking)
         //写入内容       
        memcpy(audioBuffer.i8, buffer, toWrite);
//写入结尾,通知共享内存结构体,已经写入,防止读取错误数据。
        releaseBuffer(&audioBuffer);
    }

obtainBuffer主要是通过AudioTrackClientProxy这个客户端共享内存控制的,
大概代码如下:

status_t ClientProxy::obtainBuffer(Buffer* buffer, const struct timespec *requested,
        struct timespec *elapsed)
{

//核心是这里,方便循环数组的下标的变化。是把长的坐标去掉头部。
rear &= mFrameCountP2 - 1;

//这是取的是AudioTrack,所以需要在结尾添加数据,所以是rear。
            buffer->mRaw = part1 > 0 ?
                    &((char *) mBuffers)[(rear) * mFrameSize] : NULL;

关于releaseBuffer这里就不在详细介绍,因为主要是加锁,然后通知数组下标变化的,具体逻辑这里不详细介绍。

关于循环读取的过程,本篇文章不再详细介绍,主要是通过track类中的getNextBuffer实现的,他主要是audiomixer中被调用,本质上都是通过audiomixerthread这个线程实现的,audiomix通过职责链模式,对声音进行处理,最终写入到hal层。

后记

这篇文章,这篇文章虽说不够完善,但是基本上解释了声音的大概流向,但是framew层用到比较多的系统组件,还有更底层的锁与同步机制,完全详细介绍清楚,还是分困难了,这里暂时就这样,以后如果有空,进一步补充。文章来源地址https://www.toymoban.com/news/detail-541693.html

到了这里,关于AudioTrack的声音输出流程的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【音视频原理】音视频 “ 采样 - 编码 - 封装 过程 “ 和 “ 解封装 - 解码 - 播放 过程 “ 分析 ( 视频采集处理流程 | 音频采集处理流程 | 音视频文件解封装播放流程 )

    本篇文件主要分析 音视频文件 是怎么产生的 , 以及 音视频文件是如何播放的 ; 视频文件从录像到生成文件的全过程 : 采集图像帧 : 摄像头 硬件 负责 采集画面 , 采集的 初始画面 称为 \\\" 图像帧 \\\" , 一秒钟 采集 的 图像帧 数量 称为 \\\" 帧率 \\\" , 如 : 60 帧 就是 一秒钟采集 60 个画

    2024年02月11日
    浏览(80)
  • WebRTC音视频通话-WebRTC推拉流过程中日志log输出

    WebRTC音视频通话-WebRTC推拉流过程中日志log输出 之前实现iOS端调用ossrs服务实现推拉流流程。 推流:https://blog.csdn.net/gloryFlow/article/details/132262724 拉流:https://blog.csdn.net/gloryFlow/article/details/132417602 在推拉流过程中的WebRTC的相关日志log输出可以看到一些相关描述信息。在WebRTC日志

    2024年02月10日
    浏览(61)
  • 精选58道——Android 音视频面试题_安卓音视频面试题(3)

    先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7 深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前! 因此收集整理了一份《2024年最新Android移动开发全套学习资

    2024年04月28日
    浏览(57)
  • 5G时代下,Android音视频强势崛起,我们该如何快速入门音视频技术?

    作为Android开发者的我们到底应不应该上音视频这条船? 接下来一起分析下。 大趋势 从未来的大趋势来看,随着5G时代的到来,音视频慢慢变成人们日常生活中的必需品。除了在线教育、音视频会议、即时通讯这些必须使用音视频技术的产品外,其它的产品也需要加入音频、

    2024年04月15日
    浏览(79)
  • 音视频八股文(11)-- ffmpeg avio 内存输入和内存输出。内存输出有完整代码,网上很少有的。

    avio是FFmpeg中的一个模块,用于实现多种输入输出方式的封装。 avio提供了一系列API,可以将数据从内存读取到缓冲区中,也可以将缓冲区中的数据写入到内存中。其实现依赖于IOContext结构体,该结构体定义了当前输入/输出事件的状态、数据、回调函数等信息,并支持通过自定

    2024年02月03日
    浏览(49)
  • Android音视频编码(2)

    Android本身提供了音视频编解码工具,很多时候是不需要第三方工具的,比如 ffmpeg , OpenCV 等,在android中引入第三库比较复杂,在Android音视频编码中介绍了如何引入第三方库libpng来进行进行图片处理,同时引入这些第三方库,是程序结构变得复杂。 本文介绍的音视频编解码利

    2024年01月17日
    浏览(52)
  • Android音视频-MediaCodec

    原文:https://mp.weixin.qq.com/s?__biz=MzU3NTA3MDU1OQ==mid=2247484865idx=1sn=174b8ca702466e83e72c7115d91b06eachksm=fd298df1ca5e04e7b2df9dc9f21e5cfe3e910204c905d8605f648ce6f6404432a83ae52a23a3scene=178cur_album_id=1638784435628064770#rd MediaCodec 支持处理三种数据类型,分别是压缩数据(compressed data)、原始音频数据(raw audio d

    2023年04月08日
    浏览(80)
  • Android音视频: 引入FFmpeg

    本文你可以了解到 本文将介绍如何将上一篇文章编译出来的  FFmpeg so  库,引入到  Android  工程中,并验证  so  是否可以正常使用。 一、开启 Android 原生 C/C++ 支持 在过去,通常使用  makefile  的方式在项目中引入  C/C++  代码支持,随着  Android Studio  的普及, makefile  的

    2024年02月02日
    浏览(71)
  • Android音视频之协议介绍

    本文对音视频的协议起源做详细介绍,学习之后可以加深对音视频知识的了解。 这里的音视频不仅针对Android平台,其他平台也通用。 一般是指以某种格式封装了音视频数据的文件 常见的音频格式:mp3、wma、avi、rm、rmvb、flv、mpg、mov、mkv等。 常见的视频格式:rmvb、rm、wmv、

    2023年04月19日
    浏览(57)
  • Android之 集成音视频通话

    一,背景 1.1 最近接收一个即时通讯二开项目,即时通讯部分用的XMPP协议,音视频则是集成的国外的开源免费库jitsi-meet-sdk-2.4.0-4.aar,是基于WebRTC的开源框架。但客户想要微信那种页面的排版,后来经研究jitsi是不能修改UI的,UI部分是用混合框架ReactNative写的,这样难度就大了

    2024年02月12日
    浏览(65)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包