Android Framework Audio Subsystem (05) Caso de uso de AudioTrack

Esta serie de artículos enlace Maestro: sub-directorio temático Android Framework Subsistema de audio


Resumen y descripción de puntos clave en este capítulo:

Este capítulo se centra en ➕ La parte de AudioTrack en la esquina superior izquierda del mapa mental anterior es suficiente. Analiza principalmente los dos procedimientos de prueba (shared_mem_test en la capa C ++ y MediaAudioTrackTest.java en la capa Java) y luego analiza el proceso de llamada (proceso del constructor y proceso de escritura) desde la capa Java AudioTrack a la capa C ++ AudioTrack.


Aquí se utilizan dos programas de prueba, a saber, shared_mem_test en la capa C ++ y MediaAudioTrackTest.java en la capa Java. Los directorios de estos dos programas de prueba son:

  • frameworks / base / media / tests / audiotests / shared_mem_test.cpp
  • frameworks / base / media / tests / mediaframeworktest / src / com / android / mediaframeworktest / functional / audio / MediaAudioTrackTest.java

1 análisis del programa de prueba shared_mem_test

La función principal de la entrada del programa de prueba shared_mem_test es:

int main(int argc, char *argv[]) {
    return android::main();
}

Continúe analizando, android :: main (), el código se implementa de la siguiente manera:

int main() {
    ProcessState::self()->startThreadPool();
    AudioTrackTest *test;

    test = new AudioTrackTest();
    test->Execute();
    delete test;

    return 0;
}

Aquí analizamos principalmente dos puntos clave: el constructor AudioTrackTest y su método Execute.

@ 1 constructor AudioTrackTest

El código del constructor AudioTrackTest se implementa de la siguiente manera:

AudioTrackTest::AudioTrackTest(void) {
    InitSine();         // init sine table
}

Continúe analizando InitSine, la implementación del código es la siguiente:

void AudioTrackTest::InitSine(void) {
    double phi = 0;
    double dPhi = 2 * M_PI / SIN_SZ;
    for(int i0 = 0; i0<SIN_SZ; i0++) {
        long d0;

        d0 = 32768. * sin(phi);
        phi += dPhi;
        if(d0 >= 32767) d0 = 32767;
        if(d0 <= -32768) d0 = -32768;
        sin1024[i0] = (short)d0;
    }
}

Esto es principalmente para construir datos y formas de onda para prepararse para el siguiente paso.

@ 2 Método de ejecución de AudioTrackTest

El método Execute de AudioTrackTest se implementa de la siguiente manera:

void AudioTrackTest::Execute(void) {
    if (Test01() == 0) {
        ALOGD("01 passed\n");
    } else {
        ALOGD("01 failed\n");
    }
}

Continúe analizando Test01, la implementación del código es la siguiente:

int AudioTrackTest::Test01() {

    sp<MemoryDealer> heap;
    sp<IMemory> iMem;
    uint8_t* p;

    short smpBuf[BUF_SZ];
    long rate = 44100;
    unsigned long phi;
    unsigned long dPhi;
    long amplitude;
    long freq = 1237;
    float f0;

    f0 = pow(2., 32.) * freq / (float)rate;
    dPhi = (unsigned long)f0;
    amplitude = 1000;
    phi = 0;
    Generate(smpBuf, BUF_SZ, amplitude, phi, dPhi);  // fill buffer

    for (int i = 0; i < 1024; i++) {
        heap = new MemoryDealer(1024*1024, "AudioTrack Heap Base");

        iMem = heap->allocate(BUF_SZ*sizeof(short));

        p = static_cast<uint8_t*>(iMem->pointer());
        memcpy(p, smpBuf, BUF_SZ*sizeof(short));
        //关键点1:创建一个AudioTrack对象,并传递数据
        sp<AudioTrack> track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type
               rate,
               AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
               AUDIO_CHANNEL_OUT_MONO,
               iMem);

        status_t status = track->initCheck();
        if(status != NO_ERROR) {
            track.clear();
            ALOGD("Failed for initCheck()");
            return -1;
        }

        //关键点2:开始播放
        track->start();
        usleep(20000);

        ALOGD("stop");
        track->stop();
        iMem.clear();
        heap.clear();
        usleep(20000);
    }
    return 0;
}

A través del análisis anterior, podemos saber que el proceso de uso de AudioTrack de la capa nativa es:

  1. Construya parámetros y páselos a AudioTrack. Tenga en cuenta que aquí iMem contiene Buffer con datos
  2. Realice la operación de inicio para comenzar la reproducción

2 Análisis del programa de prueba MediaAudioTrackTest

Hay muchos casos de prueba en MediaAudioTrackTest, uno de los cuales se analiza aquí, la implementación del código es la siguiente:

public class MediaAudioTrackTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {    
    private String TAG = "MediaAudioTrackTest";
    //...
    //Test case 1: setPlaybackHeadPosition() on playing track
    @LargeTest
    public void testSetPlaybackHeadPositionPlaying() throws Exception {
        // constants for test
        final String TEST_NAME = "testSetPlaybackHeadPositionPlaying";
        final int TEST_SR = 22050;
        final int TEST_CONF = AudioFormat.CHANNEL_OUT_MONO;
        final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
        final int TEST_MODE = AudioTrack.MODE_STREAM;
        final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
        
        //-------- initialization --------------
        int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
        //关键点1:构建并传递参数给AudioTrack对象
        AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 
                2*minBuffSize, TEST_MODE);
        byte data[] = new byte[minBuffSize];
        //--------    test        --------------
        assumeTrue(TEST_NAME, track.getState() == AudioTrack.STATE_INITIALIZED);
        track.write(data, 0, data.length);
        track.write(data, 0, data.length);
        //关键点2:播放
        track.play();
        assertTrue(TEST_NAME,
                track.setPlaybackHeadPosition(10) == AudioTrack.ERROR_INVALID_OPERATION);
        //-------- tear down      --------------
        track.release();
    }
    //...
}

A través del análisis anterior, podemos saber que el proceso de uso de AudioTrack en la capa Java es:

  1. Construye parámetros y pasa a AudioTrack.
  2. Realizar wrtie para escribir datos es equivalente a iMem en shared_mem_test.
  3. Realice la operación de inicio para iniciar la reproducción.

A continuación analizamos el proceso de llamada de AudioTrack desde la capa Java a la Pista de audio de la capa C ++, uno es el constructor y el otro es la función de escritura.


3 Análisis de AudioTrack de la capa Java a AudioTrack de la capa C ++

3.1 Análisis del constructor de AudioTrack de capa Java a AudioTrack de capa C ++

Al mismo tiempo, la creación del objeto AudioTrack en la capa Java finalmente conduce a la creación del objeto AudioTrack en la capa Native. Analice AudioTrack en la capa Java. La implementación del código es la siguiente:

public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
        int mode, int sessionId)
                throws IllegalArgumentException {
	//...
    // native initialization
    int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
            mSampleRate, mChannels, mAudioFormat,
            mNativeBufferSizeInBytes, mDataLoadMode, session);
    //...
    mSessionId = session[0];
    if (mDataLoadMode == MODE_STATIC) {
        mState = STATE_NO_STATIC_DATA;
    } else {
        mState = STATE_INITIALIZED;
    }
}

De acuerdo con la relación de mapeo de JNI:

{"native_setup",     "(Ljava/lang/Object;Ljava/lang/Object;IIIII[I)I",
                                         (void *)android_media_AudioTrack_setup},

Continúe analizando la implementación de android_media_AudioTrack_setup, el código es el siguiente:

static jint
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this,
        jobject jaa,
        jint sampleRateInHertz, jint javaChannelMask,
        jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession) {
	//...
    //关键点1:创建native AudioTrack对象
    sp<AudioTrack> lpTrack = new AudioTrack();
	//...
    switch (memoryMode) {//这里开始针对 两种模式MODE_STREAM 和 MODE_STATIC进行不同参数的设置
    case MODE_STREAM:
        //关键点2.1:set方法,设置参数
        status = lpTrack->set(
                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                sampleRateInHertz,
                format,// word length, PCM
                nativeChannelMask,
                frameCount,
                AUDIO_OUTPUT_FLAG_NONE,
                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
                AudioTrack::TRANSFER_SYNC,
                NULL,                         // default offloadInfo
                -1, -1,                       // default uid, pid values
                paa);
        break;
    case MODE_STATIC:
        //应用端申请共享内存
        if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
            ALOGE("Error creating AudioTrack in static mode: error creating mem heap base");
            goto native_init_failure;
        }
        //关键点2.2:set方法,设置参数
        status = lpTrack->set(
                AUDIO_STREAM_DEFAULT,// stream type, but more info conveyed in paa (last argument)
                sampleRateInHertz,
                format,// word length, PCM
                nativeChannelMask,
                frameCount,
                AUDIO_OUTPUT_FLAG_NONE,
                audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user));
                0,// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
                lpJniStorage->mMemBase,// shared mem
                true,// thread can call Java
                sessionId,// audio session ID
                AudioTrack::TRANSFER_SHARED,
                NULL,                         // default offloadInfo
                -1, -1,                       // default uid, pid values
                paa);
        break;
	//...
    default:
        ALOGE("Unknown mode %d", memoryMode);
        goto native_init_failure;
    }
	//...
    return (jint) AUDIOTRACK_ERROR_SETUP_NATIVEINITFAILED;
}

Aquí se realizan dos operaciones clave, una es crear el objeto AudioTrack de la capa C ++, y la otra es ejecutar su método de configuración, y el código del constructor del objeto AudioTrack de la capa C ++ es el siguiente:

AudioTrack::AudioTrack()//无参对象,后再调用set方法
    : mStatus(NO_INIT),
      mIsTimed(false),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0)
{
    mAttributes.content_type = AUDIO_CONTENT_TYPE_UNKNOWN;
    mAttributes.usage = AUDIO_USAGE_UNKNOWN;
    mAttributes.flags = 0x0;
    strcpy(mAttributes.tags, "");
}

AudioTrack::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,
        uint32_t notificationFrames,
        int sessionId,
        transfer_type transferType,
        const audio_offload_info_t *offloadInfo,
        int uid,
        pid_t pid,
        const audio_attributes_t* pAttributes)
    : mStatus(NO_INIT),
      mIsTimed(false),
      mPreviousPriority(ANDROID_PRIORITY_NORMAL),
      mPreviousSchedulingGroup(SP_DEFAULT),
      mPausedPosition(0)
{
    mStatus = set(streamType, sampleRate, format, channelMask,
            frameCount, flags, cbf, user, notificationFrames,
            0 /*sharedBuffer*/, false /*threadCanCallJava*/, sessionId, transferType,
            offloadInfo, uid, pid, pAttributes);
}

Puede ver que hay dos situaciones para el uso de AudioTrack:

  • Sin constructor de parámetros, más tarde necesita ejecutar el método set para establecer los parámetros.
  • Hay un constructor de parámetros, que llamará directamente al método set para establecer los parámetros.

En resumen: los objetos AudioTrack deben crearse al reproducir sonido Los objetos Java AudioTrack harán que se creen objetos c ++ AudioTrack; por lo tanto, el núcleo del análisis es la clase C ++ AudioTrack, que implica una función importante: establecer. Al mismo tiempo, AudioTrack solo puede determinar los atributos del sonido, pero no desde qué dispositivo se reproduce el sonido. La función set se introduce en detalle en el Capítulo 6.

3.2 AudioTrack de capa Java de la función de escritura en AudioTrack de capa C ++

Hay muchas funciones de escritura aquí, como sigue:

public int write(byte[] audioData, int offsetInBytes, int sizeInBytes) {
    //...
    int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,
            true /*isBlocking*/);
    //...
    return ret;
}

public int write(short[] audioData, int offsetInShorts, int sizeInShorts) {
    //...
    int ret = native_write_short(audioData, offsetInShorts, sizeInShorts, mAudioFormat);
    //...
    return ret;
}

public int write(float[] audioData, int offsetInFloats, int sizeInFloats,
        @WriteMode int writeMode) {
    //...
    int ret = native_write_float(audioData, offsetInFloats, sizeInFloats, mAudioFormat,
            writeMode == WRITE_BLOCKING);
    //...
    return ret;
}

public int write(ByteBuffer audioData, int sizeInBytes,
        @WriteMode int writeMode) {
    int ret = 0;
    if (audioData.isDirect()) {
        ret = native_write_native_bytes(audioData,
                audioData.position(), sizeInBytes, mAudioFormat,
                writeMode == WRITE_BLOCKING);
    } else {
        ret = native_write_byte(NioUtils.unsafeArray(audioData),
                NioUtils.unsafeArrayOffset(audioData) + audioData.position(),
                sizeInBytes, mAudioFormat,
                writeMode == WRITE_BLOCKING);
    }
    return ret;
}

Aquí están las llamadas de la capa nativa, continúe el seguimiento, el código es el siguiente:

static jint android_media_AudioTrack_write_byte(JNIEnv *env,  jobject thiz,
                                                  jbyteArray javaAudioData,
                                                  jint offsetInBytes, jint sizeInBytes,
                                                  jint javaAudioFormat,
                                                  jboolean isWriteBlocking) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, cAudioData, offsetInBytes, sizeInBytes,
            isWriteBlocking == JNI_TRUE /* blocking */);
    //...
    return written;
}

static jint android_media_AudioTrack_write_native_bytes(JNIEnv *env,  jobject thiz,
        jbyteArray javaBytes, jint byteOffset, jint sizeInBytes,
        jint javaAudioFormat, jboolean isWriteBlocking) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, bytes.get(), byteOffset,
            sizeInBytes, isWriteBlocking == JNI_TRUE /* blocking */);
    return written;
}

static jint android_media_AudioTrack_write_short(JNIEnv *env,  jobject thiz,
                                                  jshortArray javaAudioData,
                                                  jint offsetInShorts, jint sizeInShorts,
                                                  jint javaAudioFormat) {
    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
                                offsetInShorts * sizeof(short), sizeInShorts * sizeof(short),
            true /*blocking write, legacy behavior*/);
    //...
    return written;
}

static jint android_media_AudioTrack_write_float(JNIEnv *env,  jobject thiz,
                                                  jfloatArray javaAudioData,
                                                  jint offsetInFloats, jint sizeInFloats,
                                                  jint javaAudioFormat,
                                                  jboolean isWriteBlocking) {

    sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
    //...
    jint written = writeToTrack(lpTrack, javaAudioFormat, (jbyte *)cAudioData,
                                offsetInFloats * sizeof(float), sizeInFloats * sizeof(float),
                                isWriteBlocking == JNI_TRUE /* blocking */);
    //...
    return written;
}

Aquí las funciones nativas relacionadas finalmente llaman a la función writeToTrack, la implementación del código es la siguiente:

jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, const jbyte* data,
                  jint offsetInBytes, jint sizeInBytes, bool blocking = true) {
    ssize_t written = 0;
    //playbackthread提供共享内存,调用C++层track的write函数
    if (track->sharedBuffer() == 0) {
        written = track->write(data + offsetInBytes, sizeInBytes, blocking);
        if (written == (ssize_t) WOULD_BLOCK) {
            written = 0;
        }
    } else {//应用端 提供共享内存,直接执行memcpy
        const audio_format_t format = audioFormatToNative(audioFormat);
        switch (format) {

        default:
        case AUDIO_FORMAT_PCM_FLOAT:
        case AUDIO_FORMAT_PCM_16_BIT: {
            if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size();
            }
            //这里将data数据拷贝给 共享内存
            memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
            written = sizeInBytes;
            } break;

        case AUDIO_FORMAT_PCM_8_BIT: {
            //功能同上,只是8位需要中间的数据转换环节
            if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size() / 2;
            }
            int count = sizeInBytes;
            int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
            const uint8_t *src = (const uint8_t *)(data + offsetInBytes);
            memcpy_to_i16_from_u8(dst, src, count);
            written = sizeInBytes;
            } break;
        }
    }
    return written;
}

A continuación, llamaré algunas operaciones básicas de la pista de capa C ++. Este código se interpreta brevemente de la siguiente manera:

  1. Si track-> sharedBuffer () == 0, es decir, el hilo de reproducción proporciona memoria compartida, se ejecuta el método de escritura de la pista de capa C ++.
  2. Si track-> sharedBuffer ()! = 0, es decir, la memoria compartida es proporcionada por el lado de la APLICACIÓN, luego ejecute directamente la operación memcpy y asigne un valor a track-> sharedBuffer ().

En el capítulo 7 presentamos el método de escritura de pista en detalle .

 

 

 

 

Publicado 289 artículos originales · elogiados 47 · 30,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/vviccc/article/details/105286614
Recomendado
Clasificación