AAudioServiceStreamMMAP.cpp revision 4dbbda2d8c84eabc0cd7c7ee032c2280258f4ffb
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 "AAudioServiceStreamMMAP"
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(const android::AudioClient& serviceClient,
45                                                 bool inService)
46        : AAudioServiceStreamBase()
47        , mMmapStreamCallback(new MyMmapStreamCallback(*this))
48        , mPreviousFrameCounter(0)
49        , mMmapStream(nullptr)
50        , mServiceClient(serviceClient)
51        , mInService(inService) {
52}
53
54aaudio_result_t AAudioServiceStreamMMAP::close() {
55    if (mState == AAUDIO_STREAM_STATE_CLOSED) {
56        return AAUDIO_OK;
57    }
58    stop();
59    if (mMmapStream != 0) {
60        mMmapStream.clear(); // TODO review. Is that all we have to do?
61        // Apparently the above close is asynchronous. An attempt to open a new device
62        // right after a close can fail. Also some callbacks may still be in flight!
63        // FIXME Make closing synchronous.
64        AudioClock::sleepForNanos(100 * AAUDIO_NANOS_PER_MILLISECOND);
65    }
66
67    if (mAudioDataFileDescriptor != -1) {
68        ::close(mAudioDataFileDescriptor);
69        mAudioDataFileDescriptor = -1;
70    }
71
72    return AAudioServiceStreamBase::close();
73}
74
75// Open stream on HAL and pass information about the shared memory buffer back to the client.
76aaudio_result_t AAudioServiceStreamMMAP::open(const aaudio::AAudioStreamRequest &request,
77                                       aaudio::AAudioStreamConfiguration &configurationOutput) {
78    const audio_attributes_t attributes = {
79        .content_type = AUDIO_CONTENT_TYPE_MUSIC,
80        .usage = AUDIO_USAGE_MEDIA,
81        .source = AUDIO_SOURCE_VOICE_RECOGNITION,
82        .flags = AUDIO_FLAG_LOW_LATENCY,
83        .tags = ""
84    };
85    audio_config_base_t config;
86
87    aaudio_result_t result = AAudioServiceStreamBase::open(request, configurationOutput);
88    if (result != AAUDIO_OK) {
89        ALOGE("AAudioServiceStreamBase open returned %d", result);
90        return result;
91    }
92
93    const AAudioStreamConfiguration &configurationInput = request.getConstantConfiguration();
94    audio_port_handle_t deviceId = configurationInput.getDeviceId();
95    aaudio_direction_t direction = request.getDirection();
96
97    // Fill in config
98    aaudio_format_t aaudioFormat = configurationInput.getFormat();
99    if (aaudioFormat == AAUDIO_UNSPECIFIED || aaudioFormat == AAUDIO_FORMAT_PCM_FLOAT) {
100        aaudioFormat = AAUDIO_FORMAT_PCM_I16;
101    }
102    config.format = AAudioConvert_aaudioToAndroidDataFormat(aaudioFormat);
103
104    int32_t aaudioSampleRate = configurationInput.getSampleRate();
105    if (aaudioSampleRate == AAUDIO_UNSPECIFIED) {
106        aaudioSampleRate = AAUDIO_SAMPLE_RATE_DEFAULT;
107    }
108    config.sample_rate = aaudioSampleRate;
109
110    int32_t aaudioSamplesPerFrame = configurationInput.getSamplesPerFrame();
111
112    if (direction == AAUDIO_DIRECTION_OUTPUT) {
113        config.channel_mask = (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
114                            ? AUDIO_CHANNEL_OUT_STEREO
115                            : audio_channel_out_mask_from_count(aaudioSamplesPerFrame);
116    } else if (direction == AAUDIO_DIRECTION_INPUT) {
117        config.channel_mask =  (aaudioSamplesPerFrame == AAUDIO_UNSPECIFIED)
118                            ? AUDIO_CHANNEL_IN_STEREO
119                            : audio_channel_in_mask_from_count(aaudioSamplesPerFrame);
120    } else {
121        ALOGE("openMmapStream - invalid direction = %d", direction);
122        return AAUDIO_ERROR_ILLEGAL_ARGUMENT;
123    }
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                                                          &mPortHandle);
137    if (status != OK) {
138        ALOGE("openMmapStream returned status %d", status);
139        return AAUDIO_ERROR_UNAVAILABLE;
140    }
141
142    if (deviceId == AAUDIO_UNSPECIFIED) {
143        ALOGW("AAudioServiceStreamMMAP::open() - openMmapStream() failed to set deviceId");
144    }
145
146    // Create MMAP/NOIRQ buffer.
147    int32_t minSizeFrames = configurationInput.getBufferCapacity();
148    if (minSizeFrames <= 0) { // zero will get rejected
149        minSizeFrames = AAUDIO_BUFFER_CAPACITY_MIN;
150    }
151    status = mMmapStream->createMmapBuffer(minSizeFrames, &mMmapBufferinfo);
152    if (status != OK) {
153        ALOGE("AAudioServiceStreamMMAP::open() - createMmapBuffer() returned status %d",
154              status);
155        return AAUDIO_ERROR_UNAVAILABLE;
156    } else {
157        ALOGD("createMmapBuffer status %d, buffer_size, %d burst_size %d"
158                ", Sharable FD: %s",
159              status,
160              abs(mMmapBufferinfo.buffer_size_frames),
161              mMmapBufferinfo.burst_size_frames,
162              mMmapBufferinfo.buffer_size_frames < 0 ? "Yes" : "No");
163    }
164
165    mCapacityInFrames = mMmapBufferinfo.buffer_size_frames;
166    // FIXME: the audio HAL indicates if the shared memory fd can be shared outside of audioserver
167    // by returning a negative buffer size
168    if (mCapacityInFrames < 0) {
169        // Exclusive mode is possible from any client
170        mCapacityInFrames = -mCapacityInFrames;
171    } else {
172        // exclusive mode is only possible if the final fd destination is inside audioserver
173        if ((mMmapClient.clientUid != mServiceClient.clientUid) &&
174                configurationInput.getSharingMode() == AAUDIO_SHARING_MODE_EXCLUSIVE) {
175            // Fallback is handled by caller but indicate what is possible in case
176            // this is used in the future
177            configurationOutput.setSharingMode(AAUDIO_SHARING_MODE_SHARED);
178            return AAUDIO_ERROR_UNAVAILABLE;
179        }
180    }
181
182    // Get information about the stream and pass it back to the caller.
183    mSamplesPerFrame = (direction == AAUDIO_DIRECTION_OUTPUT)
184                           ? audio_channel_count_from_out_mask(config.channel_mask)
185                           : audio_channel_count_from_in_mask(config.channel_mask);
186
187    mAudioDataFileDescriptor = mMmapBufferinfo.shared_memory_fd;
188    mFramesPerBurst = mMmapBufferinfo.burst_size_frames;
189    mAudioFormat = AAudioConvert_androidToAAudioDataFormat(config.format);
190    mSampleRate = config.sample_rate;
191
192    // Scale up the burst size to meet the minimum equivalent in microseconds.
193    // This is to avoid waking the CPU too often when the HW burst is very small
194    // or at high sample rates.
195    int32_t burstMinMicros = AAudioProperty_getHardwareBurstMinMicros();
196    int32_t burstMicros = 0;
197    do {
198        if (burstMicros > 0) {  // skip first loop
199            mFramesPerBurst *= 2;
200        }
201        burstMicros = mFramesPerBurst * static_cast<int64_t>(1000000) / mSampleRate;
202    } while (burstMicros < burstMinMicros);
203
204    ALOGD("AAudioServiceStreamMMAP::open() original burst = %d, minMicros = %d, final burst = %d\n",
205          mMmapBufferinfo.burst_size_frames, burstMinMicros, mFramesPerBurst);
206
207    ALOGD("AAudioServiceStreamMMAP::open() actual rate = %d, channels = %d, deviceId = %d\n",
208          mSampleRate, mSamplesPerFrame, deviceId);
209
210    // Fill in AAudioStreamConfiguration
211    configurationOutput.setSampleRate(mSampleRate);
212    configurationOutput.setSamplesPerFrame(mSamplesPerFrame);
213    configurationOutput.setFormat(mAudioFormat);
214    configurationOutput.setDeviceId(deviceId);
215
216    setState(AAUDIO_STREAM_STATE_OPEN);
217    return AAUDIO_OK;
218}
219
220/**
221 * Start the flow of data.
222 */
223aaudio_result_t AAudioServiceStreamMMAP::start() {
224    if (isRunning()) {
225        return AAUDIO_OK;
226    }
227    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
228    aaudio_result_t result;
229    status_t status = mMmapStream->start(mServiceClient, &mPortHandle);
230    if (status != OK) {
231        ALOGE("AAudioServiceStreamMMAP::start() mMmapStream->start() returned %d", status);
232        disconnect();
233        result = AAudioConvert_androidToAAudioResult(status);
234    } else {
235        result = AAudioServiceStreamBase::start();
236        if (!mInService && result == AAUDIO_OK) {
237            startClient(mMmapClient, &mClientHandle);
238        }
239    }
240    return result;
241}
242
243/**
244 * Stop the flow of data such that start() can resume with loss of data.
245 */
246aaudio_result_t AAudioServiceStreamMMAP::pause() {
247    if (!isRunning()) {
248        return AAUDIO_OK;
249    }
250    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
251    aaudio_result_t result1 = AAudioServiceStreamBase::pause();
252    if (!mInService) {
253        stopClient(mClientHandle);
254    }
255    status_t status = mMmapStream->stop(mPortHandle);
256    mFramesRead.reset32();
257    return (result1 != AAUDIO_OK) ? result1 : AAudioConvert_androidToAAudioResult(status);
258}
259
260aaudio_result_t AAudioServiceStreamMMAP::stop() {
261    if (!isRunning()) {
262        return AAUDIO_OK;
263    }
264    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
265    aaudio_result_t result1 = AAudioServiceStreamBase::stop();
266    if (!mInService) {
267        stopClient(mClientHandle);
268    }
269    aaudio_result_t status = mMmapStream->stop(mPortHandle);
270    mFramesRead.reset32();
271    return (result1 != AAUDIO_OK) ? result1 :  AAudioConvert_androidToAAudioResult(status);
272}
273
274/**
275 *  Discard any data held by the underlying HAL or Service.
276 */
277aaudio_result_t AAudioServiceStreamMMAP::flush() {
278    if (mMmapStream == nullptr) return AAUDIO_ERROR_NULL;
279    // TODO how do we flush an MMAP/NOIRQ buffer? sync pointers?
280    return AAudioServiceStreamBase::flush();;
281}
282
283aaudio_result_t AAudioServiceStreamMMAP::startClient(const android::AudioClient& client,
284                                                     audio_port_handle_t *clientHandle) {
285    return AAudioConvert_androidToAAudioResult(mMmapStream->start(client, clientHandle));
286}
287
288aaudio_result_t AAudioServiceStreamMMAP::stopClient(audio_port_handle_t clientHandle) {
289    return AAudioConvert_androidToAAudioResult(mMmapStream->stop(clientHandle));
290}
291
292aaudio_result_t AAudioServiceStreamMMAP::getFreeRunningPosition(int64_t *positionFrames,
293                                                                int64_t *timeNanos) {
294    struct audio_mmap_position position;
295    if (mMmapStream == nullptr) {
296        disconnect();
297        return AAUDIO_ERROR_NULL;
298    }
299    status_t status = mMmapStream->getMmapPosition(&position);
300    if (status != OK) {
301        ALOGE("sendCurrentTimestamp(): getMmapPosition() returned %d", status);
302        disconnect();
303        return AAudioConvert_androidToAAudioResult(status);
304    } else {
305        mFramesRead.update32(position.position_frames);
306        *positionFrames = mFramesRead.get();
307        *timeNanos = position.time_nanoseconds;
308    }
309    return AAUDIO_OK;
310}
311
312void AAudioServiceStreamMMAP::onTearDown() {
313    ALOGD("AAudioServiceStreamMMAP::onTearDown() called");
314    disconnect();
315};
316
317void AAudioServiceStreamMMAP::onVolumeChanged(audio_channel_mask_t channels,
318                     android::Vector<float> values) {
319    // TODO do we really need a different volume for each channel?
320    float volume = values[0];
321    ALOGD("AAudioServiceStreamMMAP::onVolumeChanged() volume[0] = %f", volume);
322    sendServiceEvent(AAUDIO_SERVICE_EVENT_VOLUME, volume);
323};
324
325void AAudioServiceStreamMMAP::onRoutingChanged(audio_port_handle_t deviceId) {
326    ALOGD("AAudioServiceStreamMMAP::onRoutingChanged() called with %d, old = %d",
327          deviceId, mDeviceId);
328    if (mDeviceId != AUDIO_PORT_HANDLE_NONE  && mDeviceId != deviceId) {
329        disconnect();
330    }
331    mDeviceId = deviceId;
332};
333
334/**
335 * Get an immutable description of the data queue from the HAL.
336 */
337aaudio_result_t AAudioServiceStreamMMAP::getDownDataDescription(AudioEndpointParcelable &parcelable)
338{
339    // Gather information on the data queue based on HAL info.
340    int32_t bytesPerFrame = calculateBytesPerFrame();
341    int32_t capacityInBytes = mCapacityInFrames * bytesPerFrame;
342    int fdIndex = parcelable.addFileDescriptor(mAudioDataFileDescriptor, capacityInBytes);
343    parcelable.mDownDataQueueParcelable.setupMemory(fdIndex, 0, capacityInBytes);
344    parcelable.mDownDataQueueParcelable.setBytesPerFrame(bytesPerFrame);
345    parcelable.mDownDataQueueParcelable.setFramesPerBurst(mFramesPerBurst);
346    parcelable.mDownDataQueueParcelable.setCapacityInFrames(mCapacityInFrames);
347    return AAUDIO_OK;
348}
349