android13/device/google/atv/audio_proxy/service/DummyBusOutputStream.cpp

119 lines
4.1 KiB
C++

// Copyright (C) 2021 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.
#include "DummyBusOutputStream.h"
#include <algorithm>
#include <aidl/device/google/atv/audio_proxy/TimeSpec.h>
#include <android-base/logging.h>
#include <unistd.h>
using aidl::device::google::atv::audio_proxy::TimeSpec;
namespace audio_proxy::service {
namespace {
constexpr int64_t kOneSecInNs = 1'000'000'000;
constexpr int64_t kOneSecInUs = 1'000'000;
constexpr int64_t kOneUSecInNs = 1'000;
int64_t timespecDelta(const timespec& newTime, const timespec& oldTime) {
int64_t deltaSec = 0;
int64_t deltaNSec = 0;
if (newTime.tv_nsec >= oldTime.tv_nsec) {
deltaSec = newTime.tv_sec - oldTime.tv_sec;
deltaNSec = newTime.tv_nsec - oldTime.tv_nsec;
} else {
deltaSec = newTime.tv_sec - oldTime.tv_sec - 1;
deltaNSec = kOneSecInNs + newTime.tv_nsec - oldTime.tv_nsec;
}
return deltaSec * kOneSecInUs + deltaNSec / kOneUSecInNs;
}
} // namespace
DummyBusOutputStream::DummyBusOutputStream(const std::string& address,
const AidlAudioConfig& config,
int32_t flags)
: BusOutputStream(address, config, flags) {}
DummyBusOutputStream::~DummyBusOutputStream() = default;
bool DummyBusOutputStream::standby() { return true; }
bool DummyBusOutputStream::pause() { return true; }
bool DummyBusOutputStream::resume() { return true; }
bool DummyBusOutputStream::drain(AidlAudioDrain drain) { return true; }
bool DummyBusOutputStream::flush() { return true; }
bool DummyBusOutputStream::close() { return true; }
bool DummyBusOutputStream::setVolume(float left, float right) { return true; }
size_t DummyBusOutputStream::availableToWrite() {
return mWritingFrameSize * mWritingFrameCount;
}
AidlWriteStatus DummyBusOutputStream::writeRingBuffer(const uint8_t* firstMem,
size_t firstLength,
const uint8_t* secondMem,
size_t secondLength) {
size_t bufferBytes = firstLength + secondLength;
int64_t numFrames = bufferBytes / getFrameSize();
int64_t durationUs = numFrames * kOneSecInUs / mConfig.sampleRateHz;
timespec now = {0, 0};
clock_gettime(CLOCK_MONOTONIC, &now);
if (mStartTime.tv_sec == 0) {
mStartTime = now;
}
// Check underrun
int64_t elapsedTimeUs = timespecDelta(now, mStartTime);
if (elapsedTimeUs > mInputUsSinceStart) {
// Underrun
mPlayedUsBeforeUnderrun += mInputUsSinceStart;
mStartTime = now;
mInputUsSinceStart = 0;
}
// Wait if buffer full.
mInputUsSinceStart += durationUs;
int64_t waitTimeUs = mInputUsSinceStart - elapsedTimeUs - mMaxBufferUs;
if (waitTimeUs > 0) {
usleep(waitTimeUs);
clock_gettime(CLOCK_MONOTONIC, &now);
}
// Calculate played frames.
int64_t playedUs =
mPlayedUsBeforeUnderrun +
std::min(timespecDelta(now, mStartTime), mInputUsSinceStart);
TimeSpec timeSpec = {now.tv_sec, now.tv_nsec};
AidlWriteStatus status;
status.written = bufferBytes;
status.position = {playedUs * mConfig.sampleRateHz / kOneSecInUs, timeSpec};
return status;
}
bool DummyBusOutputStream::prepareForWritingImpl(uint32_t frameSize,
uint32_t frameCount) {
// The `frame` here is not audio frame, it doesn't count the sample format and
// channel layout.
mMaxBufferUs = frameSize * frameCount * 10 * kOneSecInUs /
(mConfig.sampleRateHz * getFrameSize());
return true;
}
} // namespace audio_proxy::service