深入理解Audio系统

1. AudioTrack

1.1 App调用AudioTrack播放音乐

import android.media.AudioFormat;
import android.media.AudioManager;
import android.media.AudioTrack;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;

public class AudioPlayerActivity extends AppCompatActivity {
    private AudioTrack audioTrack;
    private byte[] audioData;
    private int bufferSize;
    private boolean isPlaying = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_audio_player);

        Button playButton = findViewById(R.id.btn_play_audio);
        playButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!isPlaying) {
                    isPlaying = true;
                    playAudio();
                } else {
                    stopAudio();
                }
            }
        });

        int sampleRate = 44100; // 采样率
        int channelConfig = AudioFormat.CHANNEL_OUT_STEREO; // 立体声输出
        int audioFormat = AudioFormat.ENCODING_PCM_16BIT; // 16 位 PCM 编码
     
        ① bufferSize = AudioTrack.getMinBufferSize(sampleRate, channelConfig, audioFormat);
        ② audioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, sampleRate,
                channelConfig, audioFormat, bufferSize, AudioTrack.MODE_STREAM);
    }

    private void playAudio() {
        try {
            File mp3File = new File(Environment.getExternalStorageDirectory(), "sample.mp3");
            FileInputStream fileInputStream = new FileInputStream(mp3File);
            int length = (int) mp3File.length();
            audioData = new byte[length];
            fileInputStream.read(audioData, 0, length);
            fileInputStream.close();

            ③ audioTrack.play();
            ④ audioTrack.write(audioData, 0, length);
        } catch (IOException e) {
            e.printStackTrace();
            Toast.makeText(this, "Failed to play audio", Toast.LENGTH_SHORT).show();
        }
    }

    private void stopAudio() {
        if (isPlaying) {
            isPlaying = false;
            ⑤ audioTrack.stop();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopAudio();
        ⑥ audioTrack.release();
    }
}

1.2 AudioTrack.getMinBufferSize()

        根据“frameworks/av/services/audiopolicy/common/include/policy.h”中定义,API调用的采样率的范围是400~192000

1.3 new AudioTrack

        AudioTrack的构造用了“建造者模式”的方法,创建了一个AudioTrack实例;

public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
    int bufferSizeInBytes, int mode, int sessionId)
    throws IllegalArgumentException {
    // mState already == STATE_UNINITIALIZED
    this((new AudioAttributes.Builder())
            .setLegacyStreamType(streamType)
            .build(),
        (new AudioFormat.Builder())
            .setChannelMask(channelConfig)
            .setEncoding(audioFormat)
            .setSampleRate(sampleRateInHz)
            .build(),
        bufferSizeInBytes,
        mode, sessionId);
    deprecateStreamTypeForPlayback(streamType, "AudioTrack", "AudioTrack()");
}

1.4 AudioTrack.play()

1.5 AudioTrack.write()

1.6 Audio.stop()

1.7 Audio.release()

2. AudioFlinger

        AudioFling和AudioPolicy服务常住于audioserver程序中。

// frameworks/av/media/audioserver/main_audioserver.cpp 
int main(int argc __unused, char **argv)
{
    // 省略部分代码;

#if 1
    // FIXME See bug 165702394 and bug 168511485
    const bool doLog = false;
#else
    bool doLog = (bool) property_get_bool("ro.test_harness", 0);
#endif

    pid_t childPid;
    if (doLog && (childPid = fork()) != 0) {
        // 省略父进程部分代码;
    } else {    // 子进程代码;
        android::hardware::configureRpcThreadpool(4, false /*callerWillJoin*/);
        ProcessState::self()->startThreadPool();

        const auto af = sp<AudioFlinger>::make();
        const auto afAdapter = sp<AudioFlingerServerAdapter>::make(af);
        ALOGD("%s: AudioFlinger created", __func__);

        const auto aps = sp<AudioPolicyService>::make();
        ALOGD("%s: AudioPolicy created", __func__);
        // Start initialization of internally managed audio objects such as Device Effects.
        aps->onAudioSystemReady();

        // 将 AudioFlinger和AudioPolicy注册到ServiceManager中
        sp<IServiceManager> sm = defaultServiceManager();
        sm->addService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME), afAdapter,
                false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);
        sm->addService(String16(AudioPolicyService::getServiceName()), aps,
                false /* allowIsolated */, IServiceManager::DUMP_FLAG_PRIORITY_DEFAULT);

        // 省略部分代码
        IPCThreadState::self()->joinThreadPool();
    }
}

        AudioFlinger涉及的Hal层音频设备交互的接口为DeviceHalInterface,代码位于:frameworks/av/media/libaudiohal/include/media/audiohal/DeviceHalInterface.h

        (后面再关注这个重点问题)

3. MediaPlayer

        MediaPlayer 类可用于控制音频/视频文件和流的播放。MediaPlayer 不是线程安全的,播放器实例的创建和所有访问都应在同一线程上进行。如果要注册回调,线程必须有一个 Looper。

        MediaPlayer状态图

3.1 MediaPlayer()

static {
    System.loadLibrary("media_jni");
    native_init();
}

       这个地方主要是初始化android_media_MediaPlayer.cpp中的相关内容,同步把MediaPlayer.java中与native层的相关回调接口绑定;

private MediaPlayer(Context context, int sessionId) {
    // 省略部分代码;

    /* Native setup requires a weak reference to our object.
        * It's easier to create it here than in C++.
        */
    try (ScopedParcelState attributionSourceState = attributionSource.asScopedParcelState()) {
        native_setup(new WeakReference<>(this), attributionSourceState.getParcel(),
                resolvePlaybackSessionId(context, sessionId));
    }
    baseRegisterPlayer(getAudioSessionId());
}

// 接口位于: frameworks/base/media/java/android/media/PlayerBase.java 
/**
 * Call from derived class when instantiation / initialization is successful
 */
protected void baseRegisterPlayer(int sessionId) {
    try {
        // 调用的是AudioService中的trackPlayer()接口
        mPlayerIId = getService().trackPlayer(
                new PlayerIdCard(mImplType, mAttributes, new IPlayerWrapper(this),
                        sessionId));
    } catch (RemoteException e) {
        Log.e(TAG, "Error talking to audio service, player will not be tracked", e);
    }
}

        从native层实例化MediaPlayer对象,并注册native层的监听函数;

static void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
                                       jobject jAttributionSource,
                                       jint jAudioSessionId)
{
    ALOGV("native_setup");

    Parcel* parcel = parcelForJavaObject(env, jAttributionSource);
    android::content::AttributionSourceState attributionSource;
    attributionSource.readFromParcel(parcel);

    /**
     * @brief 创建native层的MediaPlayer实例;
     * 文件位于: frameworks/av/media/libmedia/mediaplayer.cpp 
     */
    sp<MediaPlayer> mp = sp<MediaPlayer>::make(
        attributionSource, static_cast<audio_session_t>(jAudioSessionId));
    if (mp == NULL) {
        jniThrowException(env, "java/lang/RuntimeException", "Out of memory");
        return;
    }

    /**
     * @brief create new listener and give it to MediaPlayer
     * thiz: jni层的android_media_MediaPlayer
     * weak_this: java层的MediaPlayer实例;
     * 回调函数: virtual void notify(int msg, int ext1, int ext2, const Parcel *obj = NULL);
     */
    sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
    mp->setListener(listener);

    // Stow our new C++ MediaPlayer in an opaque field in the Java object.
    setMediaPlayer(env, thiz, mp);
}

3.2 MediaPlayer::create()

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值