文档章节

Android学习(九)AudioTrack(2)

lipandroid
 lipandroid
发布于 2015/10/12 17:36
字数 1586
阅读 57
收藏 0

AudioTrack2

native层中的android_media_AudioTrack_native_setup函数中创建了一个AudioTrack* lpTrack = new AudioTrack();对象,源码在AudioTrack.cpp中:

AudioTrack::AudioTrack()

    : mStatus(NO_INIT)

{

}

再看set函数,android_media_AudioTrack_native_setup中的MODE_STREAM模式参数设置如下:

   if (memoryMode == javaAudioTrackFields.MODE_STREAM) {

 

        lpTrack->set(

            atStreamType,// stream typeàSTREAM_MUSIC

            sampleRateInHertz,

            format,// word length, PCMàPCM_16

            channels,à2

            frameCount,

            0,// flags

            audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)

            0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack

            0,// shared mem

            true,// thread can call Java

            sessionId);// audio session ID

           

    }

set的源码:

status_t AudioTrack::set(

        int streamType,

        uint32_t sampleRate,

        int format,

        int channels,

        int frameCount,

        uint32_t flags,

        callback_t cbf,

        void* user,

        int notificationFrames,

        const sp<IMemory>& sharedBuffer,

        bool threadCanCallJava,

        int sessionId)

{

 

   …

audio_io_handle_t output = à audio_io_handle_t是一个int类型,通过typedef定义,值涉及到AudioFlingerAudioPolicyService,这个值主要被AudioFlinger使用,用来表示内部的工作线程索引号,AudioFlinger会根据情况创建几个工作线程,AudioSystem::getOutput会根据流类型等其他参数最终选择一个合适的工作线程,并返回其在AudioFlinger中索引号

AudioSystem::getOutput((AudioSystem::stream_type)streamType,

            sampleRate, format, channels, (AudioSystem::output_flags)flags);

 

    if (output == 0) {

        LOGE("Could not get audio output for stream type %d", streamType);

        return BAD_VALUE;

    }

 

 …

 

    // create the IAudioTrack

    status_t status = createTrack(streamType, sampleRate, format, channelCount,

                                  frameCount, flags, sharedBuffer, output, true);

 

    if (status != NO_ERROR) {

        return status;

    }

 

    if (cbf != 0) {

        mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);

        if (mAudioTrackThread == 0) {

          LOGE("Could not create callback thread");

          return NO_INIT;

        }

    }

 

 

    return NO_ERROR;

}

分析createTrack函数:

status_t AudioTrack::createTrack(

        int streamType,

        uint32_t sampleRate,

        int format,

        int channelCount,

        int frameCount,

        uint32_t flags,

        const sp<IMemory>& sharedBuffer,

        audio_io_handle_t output,

        bool enforceFrameCount)

{

    status_t status;

    const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();à得到AudioFlingerBinder代理端BpAudioFlinger

 

    sp<IAudioTrack> track = audioFlinger->createTrack(getpid(),

                                                      streamType,

                                                      sampleRate,

                                                      format,

                                                      channelCount,

                                                      frameCount,

                                                      ((uint16_t)flags) << 16,

                                                      sharedBuffer,

                                                      output,

                                                      &mSessionId,

                                                      &status);àAudioFlinger发送createTrack请求,在STREAM模式下shareBuffer为空,outputAudioSystem::getOutput获取的值,为AudioFlinger中的线程索引号,该函数返回IAudioTrack(实际类型为BpAudioTrack)对象,后续AudioFlingerAudioTrack交互即围绕IAudioTrack进行。在STREAM模式下,没有在AudioTrack端创建共享内存,AudioFlingerAudioTrack交互的共享内存最终是由AudioFlingercreateTrack创建。

 

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

    mAudioTrack.clear();

    mAudioTrack = track;

    mCblkMemory.clear();

    mCblkMemory = cblk;à cblk control block的缩写

    mCblk = static_cast<audio_track_cblk_t*>(cblk->pointer());àIMemorypointer在此处将返回共享内存的首地址,为void*类型,做强制类型转换。

    mCblk->flags |= CBLK_DIRECTION_OUT;

    if (sharedBuffer == 0) {àbuffers指向数据空间,它的起始位置加上audio_track_cblk_t的大小

        mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);

    } else {

        mCblk->buffers = sharedBuffer->pointer();

         // Force buffer full condition as data is already present in shared memory

        mCblk->stepUser(mCblk->frameCount);

    }

 

    mCblk->volumeLR = (uint32_t(uint16_t(mVolume[RIGHT] * 0x1000)) << 16) | uint16_t(mVolume[LEFT] * 0x1000);

    mCblk->sendLevel = uint16_t(mSendLevel * 0x1000);

    mAudioTrack->attachAuxEffect(mAuxEffectId);

    mCblk->bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS;

    mCblk->waitTimeMs = 0;

    mRemainingFrames = mNotificationFramesAct;

    mLatency = afLatency + (1000*mCblk->frameCount) / sampleRate;

    return NO_ERROR;

}

 

audio_track_cblk_t结构体分析:

struct audio_track_cblk_t

{

 

    // The data members are grouped so that members accessed frequently and in the same context

    // are in the same line of data cache.

                Mutex       lock;

                Condition   cv;à同步变量,初始化的时候会设置为支持跨进程共享

    volatile    uint32_t    user;à当前写位置,volatile支持跨进程

    volatile    uint32_t    server;à当前读位置

                uint32_t    userBase;

                uint32_t    serverBase;

                void*       buffers;à指向数据缓冲区的首地址

                uint32_t    frameCount;à数据缓冲区的总大小,以Frame为单位

                // Cache line boundary

                uint32_t    loopStart;à设置播放的起点和终点

                uint32_t    loopEnd;

                int         loopCount;à设置循环播放次数

    volatile    union {

                    uint16_t    volume[2];

                    uint32_t    volumeLR;

                };à音量相关

                uint32_t    sampleRate;à采样率

                // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for

                // 8 bit PCM data: in this case,  mCblk->frameSize is based on a sample size of

                // 16 bit because data is converted to 16 bit before being stored in buffer

 

                uint8_t     frameSize;à以单位Frame的数据大小

                uint8_t     channelCount;à声道数

                uint16_t    flags;

 

                uint16_t    bufferTimeoutMs; // Maximum cumulated timeout before restarting audioflinger

                uint16_t    waitTimeMs;      // Cumulated wait time

 

                uint16_t    sendLevel;

                uint16_t    reserved;

                // Cache line boundary (32 bytes)

                            audio_track_cblk_t();

                uint32_t    stepUser(uint32_t frameCount);

                bool        stepServer(uint32_t frameCount);

                void*       buffer(uint32_t offset) const;

                uint32_t    framesAvailable();

                uint32_t    framesAvailable_l();

                uint32_t    framesReady();

};

 

分析callback_t cbf 不为空,会创建这样一个线程mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);来做回调函数。

这个线程与外界数据的输入方式有关,AudioTrack支持两种数据输入方式:

  • Push方式:用户主动调用write写数据,相当于数据被pushAudioTrackMediaPlayerService一般使用这种方式提供数据。

  • Pull方式:AudioTrackThread将利用回调函数,以EVENT_MORE_DATA为参数主动从用户那里Pull数据。ToneGenerator则使用这种方式为AudioTrack提供数据。

AudioTrack回调中能用的事件类型:

    enum event_type {

        EVENT_MORE_DATA = 0, à表示AudioTrack需要更多的数据

        EVENT_UNDERRUN = 1,à表示Audio硬件处于低负荷状态

        EVENT_LOOP_END = 2, à表示已到达播放终点

        EVENT_MARKER = 3, à数据使用警戒通知,当数据使用超过某值时,会发生通知,该值可通过setMarkerPosition()设置

        EVENT_NEW_POS = 4,à数据使用进度通知,进度通知由setPositionUpdatePeriod()设置

        EVENT_BUFFER_END = 5à数据全部被消耗

    };

 

关于AudioTrackThread的定义:

    class AudioTrackThread : public Thread

    {

    public:

        AudioTrackThread(AudioTrack& receiver, bool bCanCallJava = false);

    private:

        friend class AudioTrack;

        virtual bool        threadLoop();

        virtual status_t    readyToRun();

        virtual void        onFirstRef();

        AudioTrack& mReceiver;

        Mutex       mLock;

    };

AudioTrack& mReceiver;就是创建该线程的AudioTrack

bool AudioTrack::AudioTrackThread::threadLoop()

{

    return mReceiver.processAudioBuffer(this);

}

分析mReceiver.processAudioBuffer

bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)

{

    Buffer audioBuffer;

    uint32_t frames;

    size_t writtenSize;

 

    à处理underrun的情况

    if (mActive && (mCblk->framesReady() == 0)) {

        if ((mCblk->flags & CBLK_UNDERRUN_MSK) == CBLK_UNDERRUN_OFF) {

            mCbf(EVENT_UNDERRUN, mUserData, 0);

            if (mCblk->server == mCblk->frameCount) {àserver是读设置,frameCountbuffer中的数据总和,当读位置等于数据总和时,表示数据读完了

                mCbf(EVENT_BUFFER_END, mUserData, 0);

            }

            mCblk->flags |= CBLK_UNDERRUN_ON;

            if (mSharedBuffer != 0) return false;

        }

    }

 

    à循环播放通知

    while (mLoopCount > mCblk->loopCount) {

        int loopCount = -1;

        mLoopCount--;

        if (mLoopCount >= 0) loopCount = mLoopCount;

à一次循环播放完毕,loopCount表示还剩多少次

        mCbf(EVENT_LOOP_END, mUserData, (void *)&loopCount);

    }

 

    à警戒值操作

    if (!mMarkerReached && (mMarkerPosition > 0)) {

        if (mCblk->server >= mMarkerPosition) {

            mCbf(EVENT_MARKER, mUserData, (void *)&mMarkerPosition);

            mMarkerReached = true;

        }

    }

 

    // Manage new position callback

    if (mUpdatePeriod > 0) {

        while (mCblk->server >= mNewPosition) {

           à以帧为单位的进度通知 ,例如设置每500帧通知一次,假设消费者一次读1500帧,那么会这个循环会连续通知3

mCbf(EVENT_NEW_POS, mUserData, (void *)&mNewPosition);

            mNewPosition += mUpdatePeriod;

        }

    }

 

    // If Shared buffer is used, no data is requested from client.

    if (mSharedBuffer != 0) {

        frames = 0;

    } else {

        frames = mRemainingFrames;

    }

 

    do {

 

        audioBuffer.frameCount = frames;

 

        à获得一块可写的缓冲

        status_t err = obtainBuffer(&audioBuffer, 1);

       …

 

 

        size_t reqSize = audioBuffer.size;

        mCbf(EVENT_MORE_DATA, mUserData, &audioBuffer); à从用户那里pull数据, mCbf就是回调函数

        writtenSize = audioBuffer.size;

 

        // Sanity check on returned size

        if (ssize_t(writtenSize) <= 0) {

            // The callback is done filling buffers

            // Keep this thread going to handle timed events and

            // still try to get more data in intervals of WAIT_PERIOD_MS

            // but don't just loop and block the CPU, so wait

            usleep(WAIT_PERIOD_MS*1000);

            break;

        }

        if (writtenSize > reqSize) writtenSize = reqSize;

 

        if (mFormat == AudioSystem::PCM_8_BIT && !(mFlags & AudioSystem::OUTPUT_FLAG_DIRECT)) {

            // 8 to 16 bit conversion

            const int8_t *src = audioBuffer.i8 + writtenSize-1;

            int count = writtenSize;

            int16_t *dst = audioBuffer.i16 + writtenSize-1;

            while(count--) {

                *dst-- = (int16_t)(*src--^0x80) << 8;

            }

            writtenSize <<= 1;

        }

 

        audioBuffer.size = writtenSize;

        // NOTE: mCblk->frameSize is not equal to AudioTrack::frameSize() for

        // 8 bit PCM data: in this case,  mCblk->frameSize is based on a sampel size of

        // 16 bit.

        audioBuffer.frameCount = writtenSize/mCblk->frameSize;

 

        frames -= audioBuffer.frameCount;

 

        releaseBuffer(&audioBuffer);

    }

    while (frames);

 

    if (frames == 0) {

        mRemainingFrames = mNotificationFramesAct;

    } else {

        mRemainingFrames = frames;

    }

    return true;

}

 

© 著作权归作者所有

lipandroid
粉丝 1
博文 19
码字总数 20690
作品 0
武汉
私信 提问
android只用能AudioTrack播放实时音频

还有一篇:http://blog.51cto.com/13591594/2068009 (其项目地址: https://github.com/979451341/Audio-and-video-learning-materials ) 今天,简单讲讲AudioTrack的使用方法。 1、Android......

whoisliang
2018/11/12
25
0
Android使用FFmpeg(四)--ffmpeg实现音频播放(使用AudioTrack进行播放)

关于 Android使用FFmpeg(一)--编译ffmpeg Android使用FFmpeg(二)--Android Studio配置ffmpeg Android使用FFmpeg(三)--ffmpeg实现视频播放 Android使用FFmpeg(四)--ffmpeg实现音频播放(使用A...

天王盖地虎626
01/14
24
0
Android AudioTrack播放(解码)音频

-- MediaPlayer,AudioTrack 1.MediaPlayer能够播放多种格式的声音文件,比如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer包括了AudioTrack。 2.AudioTrack仅仅能播放已经解码的PCM流,假设是文...

desaco
2018/12/26
0
0
安卓手机上 K 歌,声音延迟怎么解决?

这篇文章可以为你提供一个解决录音和播放同步问题的思路,而且解决了声音从手机传输到耳机上有延时的问题。 初识音频 在开始之前,我先简单介绍一下音频相关的基础知识,方便下文理解。 我们...

编辑部的故事
2018/07/16
2.4K
6
audiotrack 无法获取声音

我使用android 和pc 机语音的通话,pc机是写好了的。(声音未经过处理) 在android上我用的是audiorecord 和audiotrack 。android 客户端上audiorecord 和audiotrack的使用都可以。 但是and...

krole
2017/01/18
286
0

没有更多内容

加载失败,请刷新页面

加载更多

OSChina 周六乱弹 —— 早上儿子问我他是怎么来的

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @凉小生 :#今日歌曲推荐# 少点戾气,愿你和这个世界温柔以待。中岛美嘉的单曲《僕が死のうと思ったのは (曾经我也想过一了百了)》 《僕が死の...

小小编辑
今天
192
4
Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
10
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部