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 "AAudioServiceEndpointPlay"
18//#define LOG_NDEBUG 0
19#include <utils/Log.h>
20
21#include <assert.h>
22#include <map>
23#include <mutex>
24#include <utils/Singleton.h>
25
26#include "AAudioEndpointManager.h"
27#include "AAudioServiceEndpoint.h"
28#include <algorithm>
29#include <mutex>
30#include <vector>
31
32#include "core/AudioStreamBuilder.h"
33#include "AAudioServiceEndpoint.h"
34#include "AAudioServiceStreamShared.h"
35#include "AAudioServiceEndpointPlay.h"
36#include "AAudioServiceEndpointShared.h"
37#include "AAudioServiceStreamBase.h"
38
39using namespace android;  // TODO just import names needed
40using namespace aaudio;   // TODO just import names needed
41
42#define BURSTS_PER_BUFFER_DEFAULT   2
43
44AAudioServiceEndpointPlay::AAudioServiceEndpointPlay(AAudioService &audioService)
45        : mStreamInternalPlay(audioService, true) {
46    ALOGD("%s(%p) created", __func__, this);
47    mStreamInternal = &mStreamInternalPlay;
48}
49
50AAudioServiceEndpointPlay::~AAudioServiceEndpointPlay() {
51    ALOGD("%s(%p) destroyed", __func__, this);
52}
53
54aaudio_result_t AAudioServiceEndpointPlay::open(const aaudio::AAudioStreamRequest &request) {
55    aaudio_result_t result = AAudioServiceEndpointShared::open(request);
56    if (result == AAUDIO_OK) {
57        mMixer.allocate(getStreamInternal()->getSamplesPerFrame(),
58                        getStreamInternal()->getFramesPerBurst());
59
60        int32_t burstsPerBuffer = AAudioProperty_getMixerBursts();
61        if (burstsPerBuffer == 0) {
62            mLatencyTuningEnabled = true;
63            burstsPerBuffer = BURSTS_PER_BUFFER_DEFAULT;
64        }
65        int32_t desiredBufferSize = burstsPerBuffer * getStreamInternal()->getFramesPerBurst();
66        getStreamInternal()->setBufferSize(desiredBufferSize);
67    }
68    return result;
69}
70
71// Mix data from each application stream and write result to the shared MMAP stream.
72void *AAudioServiceEndpointPlay::callbackLoop() {
73    ALOGD("%s() entering >>>>>>>>>>>>>>> MIXER", __func__);
74    aaudio_result_t result = AAUDIO_OK;
75    int64_t timeoutNanos = getStreamInternal()->calculateReasonableTimeout();
76
77    // result might be a frame count
78    while (mCallbackEnabled.load() && getStreamInternal()->isActive() && (result >= 0)) {
79        // Mix data from each active stream.
80        mMixer.clear();
81
82        { // brackets are for lock_guard
83            int index = 0;
84            int64_t mmapFramesWritten = getStreamInternal()->getFramesWritten();
85
86            std::lock_guard <std::mutex> lock(mLockStreams);
87            for (const auto clientStream : mRegisteredStreams) {
88                int64_t clientFramesRead = 0;
89                bool allowUnderflow = true;
90
91                aaudio_stream_state_t state = clientStream->getState();
92                if (state == AAUDIO_STREAM_STATE_STOPPING) {
93                    allowUnderflow = false; // just read what is already in the FIFO
94                } else if (state != AAUDIO_STREAM_STATE_STARTED) {
95                    continue; // this stream is not running so skip it.
96                }
97
98                sp<AAudioServiceStreamShared> streamShared =
99                        static_cast<AAudioServiceStreamShared *>(clientStream.get());
100
101                {
102                    // Lock the AudioFifo to protect against close.
103                    std::lock_guard <std::mutex> lock(streamShared->getAudioDataQueueLock());
104
105                    FifoBuffer *fifo = streamShared->getAudioDataFifoBuffer_l();
106                    if (fifo != nullptr) {
107
108                        // Determine offset between framePosition in client's stream
109                        // vs the underlying MMAP stream.
110                        clientFramesRead = fifo->getReadCounter();
111                        // These two indices refer to the same frame.
112                        int64_t positionOffset = mmapFramesWritten - clientFramesRead;
113                        streamShared->setTimestampPositionOffset(positionOffset);
114
115                        int32_t framesMixed = mMixer.mix(index, fifo, allowUnderflow);
116
117                        if (streamShared->isFlowing()) {
118                            // Consider it an underflow if we got less than a burst
119                            // after the data started flowing.
120                            bool underflowed = allowUnderflow
121                                               && framesMixed < mMixer.getFramesPerBurst();
122                            if (underflowed) {
123                                streamShared->incrementXRunCount();
124                            }
125                        } else if (framesMixed > 0) {
126                            // Mark beginning of data flow after a start.
127                            streamShared->setFlowing(true);
128                        }
129                        clientFramesRead = fifo->getReadCounter();
130                    }
131                }
132
133                if (clientFramesRead > 0) {
134                    // This timestamp represents the completion of data being read out of the
135                    // client buffer. It is sent to the client and used in the timing model
136                    // to decide when the client has room to write more data.
137                    Timestamp timestamp(clientFramesRead, AudioClock::getNanoseconds());
138                    streamShared->markTransferTime(timestamp);
139                }
140
141                index++; // just used for labelling tracks in systrace
142            }
143        }
144
145        // Write mixer output to stream using a blocking write.
146        result = getStreamInternal()->write(mMixer.getOutputBuffer(),
147                                            getFramesPerBurst(), timeoutNanos);
148        if (result == AAUDIO_ERROR_DISCONNECTED) {
149            AAudioServiceEndpointShared::disconnectRegisteredStreams();
150            break;
151        } else if (result != getFramesPerBurst()) {
152            ALOGW("callbackLoop() wrote %d / %d",
153                  result, getFramesPerBurst());
154            break;
155        }
156    }
157
158    ALOGD("%s() exiting, enabled = %d, state = %d, result = %d <<<<<<<<<<<<< MIXER",
159          __func__, mCallbackEnabled.load(), getStreamInternal()->getState(), result);
160    return NULL; // TODO review
161}
162