AAudioServiceStreamMMAP.cpp revision 5ef003b750ae5188c36d6512584da442be125148
1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "AAudioService" 18//#define LOG_NDEBUG 0 19#include <utils/Log.h> 20 21#include <atomic> 22#include <stdint.h> 23 24#include <utils/String16.h> 25#include <media/nbaio/AudioStreamOutSink.h> 26#include <media/MmapStreamInterface.h> 27 28#include "AAudioServiceStreamBase.h" 29#include "AAudioServiceStreamMMAP.h" 30#include "binding/AudioEndpointParcelable.h" 31#include "SharedMemoryProxy.h" 32#include "utility/AAudioUtilities.h" 33 34using namespace android; 35using namespace aaudio; 36 37#define AAUDIO_BUFFER_CAPACITY_MIN 4 * 512 38#define AAUDIO_SAMPLE_RATE_DEFAULT 48000 39 40/** 41 * Service Stream that uses an MMAP buffer. 42 */ 43 44AAudioServiceStreamMMAP::AAudioServiceStreamMMAP() 45 : AAudioServiceStreamBase() 46 , mMmapStreamCallback(new MyMmapStreamCallback(*this)) 47 , mPreviousFrameCounter(0) 48 , mMmapStream(nullptr) { 49} 50 51AAudioServiceStreamMMAP::~AAudioServiceStreamMMAP() { 52 close(); 53} 54 55aaudio_result_t AAudioServiceStreamMMAP::close() { 56 mMmapStream.clear(); // TODO review. Is that all we have to do? 57 // Apparently the above close is asynchronous. An attempt to open a new device 58 // right after a close can fail. Also some callbacks may still be in flight! 59 // FIXME Make closing synchronous. 60 AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND); 61 62 if (mAudioDataFileDescriptor != -1) { 63 ::close(mAudioDataFileDescriptor); 64 mAudioDataFileDescriptor = -1; 65 } 66 67 return AAudioServiceStreamBase::close(); 68} 69 70// Open stream on HAL and pass information about the shared memory buffer back to the client. 71aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request, 72 aaudio::AAudioStreamConfiguration &configurationOutput) { 73 const audio_attributes_t attributes = { 74 .content_type = AUDIO_CONTENT_TYPE_MUSIC, 75 .usage = AUDIO_USAGE_MEDIA, 76 .source = AUDIO_SOURCE_VOICE_RECOGNITION, 77 .flags = AUDIO_FLAG_LOW_LATENCY, 78 .tags = "" 79 }; 80 audio_config_base_t config; 81 82 aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput); 83 if (result != AAUDIO_OK) { 84 ALOGE("AAudioServiceStreamBase open returned %d", result); 85 return result; 86 } 87 88 const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration(); 89 audio_port_handle_t deviceId = configurationInput.getDeviceId(); 90 91 mMmapClient.clientUid = request.getUserId(); 92 mMmapClient.clientPid = request.getProcessId(); 93 aaudio_direction_t direction = request.getDirection(); 94 95 // Fill in config 96 aaudio_format_t aaudioFormat = configurationInput.getAudioFormat(); 97 if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) { 98 aaudioFormat = AAUDIO_FORMAT_PCM_I16; 99 } 100 config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat); 101 102 int32_t aaudioSampleRate = configurationInput.getSampleRate(); 103 if (aaudioSampleRate == AAUDIO_UNSPECIFIED) { 104 aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT; 105 } 106 config.sample_rate = aaudioSampleRate; 107 108 int32_t aaudioSamplesPerFrame = configurationInput.getSamplesPerFrame(); 109 110 if (direction == AAUDIO_DIRECTION_OUTPUT) { 111 config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED) 112 ? AUDIO_CHANNEL_OUT_STEREO 113 : audio_channel_out_mask_from_count(aaudioSamplesPerFrame); 114 } else if (direction == AAUDIO_DIRECTION_INPUT) { 115 config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED) 116 ? AUDIO_CHANNEL_IN_STEREO 117 : audio_channel_in_mask_from_count(aaudioSamplesPerFrame); 118 } else { 119 ALOGE("openMmapStream - invalid direction = %d", direction); 120 return AAUDIO_ERROR_ILLEGAL_ARGUMENT; 121 } 122 123 mMmapClient.packageName.setTo(String16("aaudio_service")); // FIXME what should we do here? 124 125 MmapStreamInterface::stream_direction_t streamDirection = (direction == AAUDIO_DIRECTION_OUTPUT) 126 ? MmapStreamInterface::DIRECTION_OUTPUT : MmapStreamInterface::DIRECTION_INPUT; 127 128 // Open HAL stream. 129 status_t status = MmapStreamInterface::openMmapStream(streamDirection, 130 &attributes, 131 &config, 132 mMmapClient, 133 &deviceId, 134 mMmapStreamCallback, 135 mMmapStream); 136 if (status != OK) { 137 ALOGE("openMmapStream returned status %d", status); 138 return AAUDIO_ERROR_UNAVAILABLE; 139 } 140 141 if (deviceId == AAUDIO_UNSPECIFIED) { 142 ALOGW("AAudioServiceStreamMMAP::open() - openMmapStream() failed to set deviceId"); 143 } 144 145 // Create MMAP/NOIRQ buffer. 146 int32_t minSizeFrames = configurationInput.getBufferCapacity(); 147 if (minSizeFrames <= 0) { // zero will get rejected 148 minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN; 149 } 150 status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo); 151 if (status != OK) { 152 ALOGE("AAudioServiceStreamMMAP::open() - createMmapBuffer() returned status %d", 153 status); 154 return AAUDIO_ERROR_UNAVAILABLE; 155 } else { 156 ALOGD("createMmapBuffer status %d shared_address = %p buffer_size %d burst_size %d", 157 status, mMmapBufferinfo.shared_memory_address, 158 mMmapBufferinfo.buffer_size_frames, 159 mMmapBufferinfo.burst_size_frames); 160 } 161 162 // Get information about the stream and pass it back to the caller. 163 mSamplesPerFrame = (direction == AAUDIO_DIRECTION_OUTPUT) 164 ? audio_channel_count_from_out_mask(config.channel_mask) 165 : audio_channel_count_from_in_mask(config.channel_mask); 166 167 mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd; 168 mFramesPerBurst = mMmapBufferinfo.burst_size_frames; 169 mCapacityInFrames = mMmapBufferinfo.buffer_size_frames; 170 mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format); 171 mSampleRate = config.sample_rate; 172 173 // Scale up the burst size to meet the minimum equivalent in microseconds. 174 // This is to avoid waking the CPU too often when the HW burst is very small 175 // or at high sample rates. 176 int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros(); 177 int32_t burstMicros = 0; 178 do { 179 if (burstMicros > 0) { // skip first loop 180 mFramesPerBurst *= 2; 181 } 182 burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / mSampleRate; 183 } while (burstMicros < burstMinMicros); 184 185 ALOGD("AAudioServiceStreamMMAP::open() original burst = %d, minMicros = %d, final burst = %d\n", 186 mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst); 187 188 ALOGD("AAudioServiceStreamMMAP::open() actual rate = %d, channels = %d, deviceId = %d\n", 189 mSampleRate, mSamplesPerFrame, deviceId); 190 191 // Fill in AAudioStreamConfiguration 192 configurationOutput.setSampleRate(mSampleRate); 193 configurationOutput.setSamplesPerFrame(mSamplesPerFrame); 194 configurationOutput.setAudioFormat(mAudioFormat); 195 configurationOutput.setDeviceId(deviceId); 196 197 return AAUDIO_OK; 198} 199 200/** 201 * Start the flow of data. 202 */ 203aaudio_result_t AAudioServiceStreamMMAP::start() { 204 if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL; 205 aaudio_result_t result; 206 status_t status = mMmapStream->start(mMmapClient, &mPortHandle); 207 if (status != OK) { 208 ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status); 209 disconnect(); 210 result = AAudioConvert_androidToAAudioResult(status); 211 } else { 212 result = AAudioServiceStreamBase::start(); 213 } 214 return result; 215} 216 217/** 218 * Stop the flow of data such that start() can resume with loss of data. 219 */ 220aaudio_result_t AAudioServiceStreamMMAP::pause() { 221 if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL; 222 223 aaudio_result_t result1 = AAudioServiceStreamBase::pause(); 224 status_t status = mMmapStream->stop(mPortHandle); 225 mFramesRead.reset32(); 226 return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status); 227} 228 229aaudio_result_t AAudioServiceStreamMMAP::stop() { 230 if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL; 231 232 aaudio_result_t result1 = AAudioServiceStreamBase::stop(); 233 aaudio_result_t status = mMmapStream->stop(mPortHandle); 234 mFramesRead.reset32(); 235 return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status); 236} 237 238/** 239 * Discard any data held by the underlying HAL or Service. 240 */ 241aaudio_result_t AAudioServiceStreamMMAP::flush() { 242 if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL; 243 // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers? 244 return AAudioServiceStreamBase::flush();; 245} 246 247aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames, 248 int64_t *timeNanos) { 249 struct audio_mmap_position position; 250 if (mMmapStream == nullptr) { 251 disconnect(); 252 return AAUDIO_ERROR_NULL; 253 } 254 status_t status = mMmapStream->getMmapPosition(&position); 255 if (status != OK) { 256 ALOGE("sendCurrentTimestamp(): getMmapPosition() returned %d", status); 257 disconnect(); 258 return AAudioConvert_androidToAAudioResult(status); 259 } else { 260 mFramesRead.update32(position.position_frames); 261 *positionFrames = mFramesRead.get(); 262 *timeNanos = position.time_nanoseconds; 263 } 264 return AAUDIO_OK; 265} 266 267void AAudioServiceStreamMMAP::onTearDown() { 268 ALOGD("AAudioServiceStreamMMAP::onTearDown() called"); 269 disconnect(); 270}; 271 272void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels, 273 android::Vector<float> values) { 274 // TODO do we really need a different volume for each channel? 275 float volume = values[0]; 276 ALOGD("AAudioServiceStreamMMAP::onVolumeChanged() volume[0] = %f", volume); 277 sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume); 278}; 279 280void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) { 281 ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d", 282 deviceId, mPortHandle); 283 if (mPortHandle > 0 && mPortHandle != deviceId) { 284 disconnect(); 285 } 286 mPortHandle = deviceId; 287}; 288 289/** 290 * Get an immutable description of the data queue from the HAL. 291 */ 292aaudio_result_t AAudioServiceStreamMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable) 293{ 294 // Gather information on the data queue based on HAL info. 295 int32_t bytesPerFrame = calculateBytesPerFrame(); 296 int32_t capacityInBytes = mCapacityInFrames * bytesPerFrame; 297 int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes); 298 parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes); 299 parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame); 300 parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst); 301 parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames); 302 return AAUDIO_OK; 303} 304