/* * Copyright 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define LOG_TAG "AAudioStream" //#define LOG_NDEBUG 0 #include #include #include #include #include "AudioStreamBuilder.h" #include "AudioStream.h" #include "AudioClock.h" using namespace aaudio; AudioStream::AudioStream() : mPlayerBase(new MyPlayerBase(this)) { // mThread is a pthread_t of unknown size so we need memset. memset(&mThread, 0, sizeof(mThread)); setPeriodNanoseconds(0); } AudioStream::~AudioStream() { ALOGD("destroying %p, state = %s", this, AAudio_convertStreamStateToText(getState())); // If the stream is deleted when OPEN or in use then audio resources will leak. // This would indicate an internal error. So we want to find this ASAP. LOG_ALWAYS_FATAL_IF(!(getState() == AAUDIO_STREAM_STATE_CLOSED || getState() == AAUDIO_STREAM_STATE_UNINITIALIZED || getState() == AAUDIO_STREAM_STATE_DISCONNECTED), "aaudio stream still in use, state = %s", AAudio_convertStreamStateToText(getState())); mPlayerBase->clearParentReference(); // remove reference to this AudioStream } static const char *AudioStream_convertSharingModeToShortText(aaudio_sharing_mode_t sharingMode) { const char *result; switch (sharingMode) { case AAUDIO_SHARING_MODE_EXCLUSIVE: result = "EX"; break; case AAUDIO_SHARING_MODE_SHARED: result = "SH"; break; default: result = "?!"; break; } return result; } aaudio_result_t AudioStream::open(const AudioStreamBuilder& builder) { // Call here as well because the AAudioService will call this without calling build(). aaudio_result_t result = builder.validate(); if (result != AAUDIO_OK) { return result; } // Copy parameters from the Builder because the Builder may be deleted after this call. mSamplesPerFrame = builder.getSamplesPerFrame(); mSampleRate = builder.getSampleRate(); mDeviceId = builder.getDeviceId(); mFormat = builder.getFormat(); mSharingMode = builder.getSharingMode(); mSharingModeMatchRequired = builder.isSharingModeMatchRequired(); mPerformanceMode = builder.getPerformanceMode(); // callbacks mFramesPerDataCallback = builder.getFramesPerDataCallback(); mDataCallbackProc = builder.getDataCallbackProc(); mErrorCallbackProc = builder.getErrorCallbackProc(); mDataCallbackUserData = builder.getDataCallbackUserData(); mErrorCallbackUserData = builder.getErrorCallbackUserData(); // This is very helpful for debugging in the future. Please leave it in. ALOGI("AudioStream::open() rate = %d, channels = %d, format = %d, sharing = %s, dir = %s", mSampleRate, mSamplesPerFrame, mFormat, AudioStream_convertSharingModeToShortText(mSharingMode), (getDirection() == AAUDIO_DIRECTION_OUTPUT) ? "OUTPUT" : "INPUT"); ALOGI("AudioStream::open() device = %d, perfMode = %d, callback: %s with frames = %d", mDeviceId, mPerformanceMode, (mDataCallbackProc == nullptr ? "OFF" : "ON"), mFramesPerDataCallback); return AAUDIO_OK; } aaudio_result_t AudioStream::waitForStateChange(aaudio_stream_state_t currentState, aaudio_stream_state_t *nextState, int64_t timeoutNanoseconds) { aaudio_result_t result = updateStateMachine(); if (result != AAUDIO_OK) { return result; } int64_t durationNanos = 20 * AAUDIO_NANOS_PER_MILLISECOND; // arbitrary aaudio_stream_state_t state = getState(); while (state == currentState && timeoutNanoseconds > 0) { if (durationNanos > timeoutNanoseconds) { durationNanos = timeoutNanoseconds; } AudioClock::sleepForNanos(durationNanos); timeoutNanoseconds -= durationNanos; aaudio_result_t result = updateStateMachine(); if (result != AAUDIO_OK) { return result; } state = getState(); } if (nextState != nullptr) { *nextState = state; } return (state == currentState) ? AAUDIO_ERROR_TIMEOUT : AAUDIO_OK; } // This registers the callback thread with the server before // passing control to the app. This gives the server an opportunity to boost // the thread's performance characteristics. void* AudioStream::wrapUserThread() { void* procResult = nullptr; mThreadRegistrationResult = registerThread(); if (mThreadRegistrationResult == AAUDIO_OK) { // Run callback loop. This may take a very long time. procResult = mThreadProc(mThreadArg); mThreadRegistrationResult = unregisterThread(); } return procResult; } // This is the entry point for the new thread created by createThread(). // It converts the 'C' function call to a C++ method call. static void* AudioStream_internalThreadProc(void* threadArg) { AudioStream *audioStream = (AudioStream *) threadArg; return audioStream->wrapUserThread(); } // This is not exposed in the API. // But it is still used internally to implement callbacks for MMAP mode. aaudio_result_t AudioStream::createThread(int64_t periodNanoseconds, aaudio_audio_thread_proc_t threadProc, void* threadArg) { if (mHasThread) { ALOGE("AudioStream::createThread() - mHasThread already true"); return AAUDIO_ERROR_INVALID_STATE; } if (threadProc == nullptr) { return AAUDIO_ERROR_NULL; } // Pass input parameters to the background thread. mThreadProc = threadProc; mThreadArg = threadArg; setPeriodNanoseconds(periodNanoseconds); int err = pthread_create(&mThread, nullptr, AudioStream_internalThreadProc, this); if (err != 0) { return AAudioConvert_androidToAAudioResult(-errno); } else { mHasThread = true; return AAUDIO_OK; } } aaudio_result_t AudioStream::joinThread(void** returnArg, int64_t timeoutNanoseconds) { if (!mHasThread) { ALOGE("AudioStream::joinThread() - but has no thread"); return AAUDIO_ERROR_INVALID_STATE; } #if 0 // TODO implement equivalent of pthread_timedjoin_np() struct timespec abstime; int err = pthread_timedjoin_np(mThread, returnArg, &abstime); #else int err = pthread_join(mThread, returnArg); #endif mHasThread = false; return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult; } #if AAUDIO_USE_VOLUME_SHAPER android::media::VolumeShaper::Status AudioStream::applyVolumeShaper( const android::media::VolumeShaper::Configuration& configuration __unused, const android::media::VolumeShaper::Operation& operation __unused) { ALOGW("applyVolumeShaper() is not supported"); return android::media::VolumeShaper::Status::ok(); } #endif AudioStream::MyPlayerBase::MyPlayerBase(AudioStream *parent) : mParent(parent) { } AudioStream::MyPlayerBase::~MyPlayerBase() { ALOGV("MyPlayerBase::~MyPlayerBase(%p) deleted", this); } void AudioStream::MyPlayerBase::registerWithAudioManager() { if (!mRegistered) { init(android::PLAYER_TYPE_AAUDIO, AUDIO_USAGE_MEDIA); mRegistered = true; } } void AudioStream::MyPlayerBase::unregisterWithAudioManager() { if (mRegistered) { baseDestroy(); mRegistered = false; } } void AudioStream::MyPlayerBase::destroy() { unregisterWithAudioManager(); }