1 // Copyright (C) 2021 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "DummyBusOutputStream.h"
16 
17 #include <algorithm>
18 
19 #include <aidl/device/google/atv/audio_proxy/TimeSpec.h>
20 #include <android-base/logging.h>
21 #include <unistd.h>
22 
23 using aidl::device::google::atv::audio_proxy::TimeSpec;
24 
25 namespace audio_proxy::service {
26 namespace {
27 constexpr int64_t kOneSecInNs = 1'000'000'000;
28 constexpr int64_t kOneSecInUs = 1'000'000;
29 constexpr int64_t kOneUSecInNs = 1'000;
30 
timespecDelta(const timespec & newTime,const timespec & oldTime)31 int64_t timespecDelta(const timespec& newTime, const timespec& oldTime) {
32   int64_t deltaSec = 0;
33   int64_t deltaNSec = 0;
34   if (newTime.tv_nsec >= oldTime.tv_nsec) {
35     deltaSec = newTime.tv_sec - oldTime.tv_sec;
36     deltaNSec = newTime.tv_nsec - oldTime.tv_nsec;
37   } else {
38     deltaSec = newTime.tv_sec - oldTime.tv_sec - 1;
39     deltaNSec = kOneSecInNs + newTime.tv_nsec - oldTime.tv_nsec;
40   }
41 
42   return deltaSec * kOneSecInUs + deltaNSec / kOneUSecInNs;
43 }
44 }  // namespace
45 
DummyBusOutputStream(const std::string & address,const AidlAudioConfig & config,int32_t flags)46 DummyBusOutputStream::DummyBusOutputStream(const std::string& address,
47                                            const AidlAudioConfig& config,
48                                            int32_t flags)
49     : BusOutputStream(address, config, flags) {}
50 DummyBusOutputStream::~DummyBusOutputStream() = default;
51 
standby()52 bool DummyBusOutputStream::standby() { return true; }
pause()53 bool DummyBusOutputStream::pause() { return true; }
resume()54 bool DummyBusOutputStream::resume() { return true; }
drain(AidlAudioDrain drain)55 bool DummyBusOutputStream::drain(AidlAudioDrain drain) { return true; }
flush()56 bool DummyBusOutputStream::flush() { return true; }
close()57 bool DummyBusOutputStream::close() { return true; }
setVolume(float left,float right)58 bool DummyBusOutputStream::setVolume(float left, float right) { return true; }
59 
availableToWrite()60 size_t DummyBusOutputStream::availableToWrite() {
61   return mWritingFrameSize * mWritingFrameCount;
62 }
63 
writeRingBuffer(const uint8_t * firstMem,size_t firstLength,const uint8_t * secondMem,size_t secondLength)64 AidlWriteStatus DummyBusOutputStream::writeRingBuffer(const uint8_t* firstMem,
65                                                       size_t firstLength,
66                                                       const uint8_t* secondMem,
67                                                       size_t secondLength) {
68   size_t bufferBytes = firstLength + secondLength;
69   int64_t numFrames = bufferBytes / getFrameSize();
70   int64_t durationUs = numFrames * kOneSecInUs / mConfig.sampleRateHz;
71 
72   timespec now = {0, 0};
73   clock_gettime(CLOCK_MONOTONIC, &now);
74   if (mStartTime.tv_sec == 0) {
75     mStartTime = now;
76   }
77 
78   // Check underrun
79   int64_t elapsedTimeUs = timespecDelta(now, mStartTime);
80   if (elapsedTimeUs > mInputUsSinceStart) {
81     // Underrun
82     mPlayedUsBeforeUnderrun += mInputUsSinceStart;
83     mStartTime = now;
84     mInputUsSinceStart = 0;
85   }
86 
87   // Wait if buffer full.
88   mInputUsSinceStart += durationUs;
89   int64_t waitTimeUs = mInputUsSinceStart - elapsedTimeUs - mMaxBufferUs;
90   if (waitTimeUs > 0) {
91     usleep(waitTimeUs);
92     clock_gettime(CLOCK_MONOTONIC, &now);
93   }
94 
95   // Calculate played frames.
96   int64_t playedUs =
97       mPlayedUsBeforeUnderrun +
98       std::min(timespecDelta(now, mStartTime), mInputUsSinceStart);
99 
100   TimeSpec timeSpec = {now.tv_sec, now.tv_nsec};
101 
102   AidlWriteStatus status;
103   status.written = bufferBytes;
104   status.position = {playedUs * mConfig.sampleRateHz / kOneSecInUs, timeSpec};
105 
106   return status;
107 }
108 
prepareForWritingImpl(uint32_t frameSize,uint32_t frameCount)109 bool DummyBusOutputStream::prepareForWritingImpl(uint32_t frameSize,
110                                                  uint32_t frameCount) {
111   // The `frame` here is not audio frame, it doesn't count the sample format and
112   // channel layout.
113   mMaxBufferUs = frameSize * frameCount * 10 * kOneSecInUs /
114                  (mConfig.sampleRateHz * getFrameSize());
115   return true;
116 }
117 
start()118 bool DummyBusOutputStream::start() { return false; }
119 
stop()120 bool DummyBusOutputStream::stop() { return false; };
121 
createMmapBuffer(int32_t minBufferSizeFrames)122 AidlMmapBufferInfo DummyBusOutputStream::createMmapBuffer(
123     int32_t minBufferSizeFrames) {
124   return AidlMmapBufferInfo();
125 }
126 
getMmapPosition()127 AidlPresentationPosition DummyBusOutputStream::getMmapPosition() {
128   return AidlPresentationPosition();
129 }
130 
131 }  // namespace audio_proxy::service
132