StreamOut.impl.h revision 936279e1ffe6bf7e842c46f9a94d98a48dce6754
1/* 2 * Copyright (C) 2016 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 "StreamOutHAL" 18//#define LOG_NDEBUG 0 19#define ATRACE_TAG ATRACE_TAG_AUDIO 20 21#include <android/log.h> 22#include <hardware/audio.h> 23#include <utils/Trace.h> 24 25#include "StreamOut.h" 26 27namespace android { 28namespace hardware { 29namespace audio { 30namespace V2_0 { 31namespace implementation { 32 33using ::android::hardware::audio::common::V2_0::ThreadInfo; 34 35namespace { 36 37class WriteThread : public Thread { 38 public: 39 // WriteThread's lifespan never exceeds StreamOut's lifespan. 40 WriteThread(std::atomic<bool>* stop, 41 audio_stream_out_t* stream, 42 StreamOut::CommandMQ* commandMQ, 43 StreamOut::DataMQ* dataMQ, 44 StreamOut::StatusMQ* statusMQ, 45 EventFlag* efGroup) 46 : Thread(false /*canCallJava*/), 47 mStop(stop), 48 mStream(stream), 49 mCommandMQ(commandMQ), 50 mDataMQ(dataMQ), 51 mStatusMQ(statusMQ), 52 mEfGroup(efGroup), 53 mBuffer(new uint8_t[dataMQ->getQuantumCount()]) { 54 } 55 virtual ~WriteThread() {} 56 57 private: 58 std::atomic<bool>* mStop; 59 audio_stream_out_t* mStream; 60 StreamOut::CommandMQ* mCommandMQ; 61 StreamOut::DataMQ* mDataMQ; 62 StreamOut::StatusMQ* mStatusMQ; 63 EventFlag* mEfGroup; 64 std::unique_ptr<uint8_t[]> mBuffer; 65 IStreamOut::WriteStatus mStatus; 66 67 bool threadLoop() override; 68 69 void doGetLatency(); 70 void doGetPresentationPosition(); 71 void doWrite(); 72}; 73 74void WriteThread::doWrite() { 75 const size_t availToRead = mDataMQ->availableToRead(); 76 mStatus.retval = Result::OK; 77 mStatus.reply.written = 0; 78 if (mDataMQ->read(&mBuffer[0], availToRead)) { 79 ssize_t writeResult = mStream->write(mStream, &mBuffer[0], availToRead); 80 if (writeResult >= 0) { 81 mStatus.reply.written = writeResult; 82 } else { 83 mStatus.retval = Stream::analyzeStatus("write", writeResult); 84 } 85 } 86} 87 88void WriteThread::doGetPresentationPosition() { 89 mStatus.retval = StreamOut::getPresentationPositionImpl( 90 mStream, 91 &mStatus.reply.presentationPosition.frames, 92 &mStatus.reply.presentationPosition.timeStamp); 93} 94 95void WriteThread::doGetLatency() { 96 mStatus.retval = Result::OK; 97 mStatus.reply.latencyMs = mStream->get_latency(mStream); 98} 99 100bool WriteThread::threadLoop() { 101 // This implementation doesn't return control back to the Thread until it decides to stop, 102 // as the Thread uses mutexes, and this can lead to priority inversion. 103 while(!std::atomic_load_explicit(mStop, std::memory_order_acquire)) { 104 uint32_t efState = 0; 105 mEfGroup->wait(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState); 106 if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY))) { 107 continue; // Nothing to do. 108 } 109 if (!mCommandMQ->read(&mStatus.replyTo)) { 110 continue; // Nothing to do. 111 } 112 switch (mStatus.replyTo) { 113 case IStreamOut::WriteCommand::WRITE: 114 doWrite(); 115 break; 116 case IStreamOut::WriteCommand::GET_PRESENTATION_POSITION: 117 doGetPresentationPosition(); 118 break; 119 case IStreamOut::WriteCommand::GET_LATENCY: 120 doGetLatency(); 121 break; 122 default: 123 ALOGE("Unknown write thread command code %d", mStatus.replyTo); 124 mStatus.retval = Result::NOT_SUPPORTED; 125 break; 126 } 127 if (!mStatusMQ->write(&mStatus)) { 128 ALOGE("status message queue write failed"); 129 } 130 mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL)); 131 } 132 133 return false; 134} 135 136} // namespace 137 138StreamOut::StreamOut(const sp<Device>& device, audio_stream_out_t* stream) 139 : mIsClosed(false), mDevice(device), mStream(stream), 140 mStreamCommon(new Stream(&stream->common)), 141 mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)), 142 mEfGroup(nullptr), mStopWriteThread(false) { 143} 144 145StreamOut::~StreamOut() { 146 ATRACE_CALL(); 147 close(); 148 if (mWriteThread.get()) { 149 ATRACE_NAME("mWriteThread->join"); 150 status_t status = mWriteThread->join(); 151 ALOGE_IF(status, "write thread exit error: %s", strerror(-status)); 152 } 153 if (mEfGroup) { 154 status_t status = EventFlag::deleteEventFlag(&mEfGroup); 155 ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status)); 156 } 157 mCallback.clear(); 158 mDevice->closeOutputStream(mStream); 159 mStream = nullptr; 160} 161 162// Methods from ::android::hardware::audio::V2_0::IStream follow. 163Return<uint64_t> StreamOut::getFrameSize() { 164 return audio_stream_out_frame_size(mStream); 165} 166 167Return<uint64_t> StreamOut::getFrameCount() { 168 return mStreamCommon->getFrameCount(); 169} 170 171Return<uint64_t> StreamOut::getBufferSize() { 172 return mStreamCommon->getBufferSize(); 173} 174 175Return<uint32_t> StreamOut::getSampleRate() { 176 return mStreamCommon->getSampleRate(); 177} 178 179Return<void> StreamOut::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) { 180 return mStreamCommon->getSupportedSampleRates(_hidl_cb); 181} 182 183Return<Result> StreamOut::setSampleRate(uint32_t sampleRateHz) { 184 return mStreamCommon->setSampleRate(sampleRateHz); 185} 186 187Return<AudioChannelMask> StreamOut::getChannelMask() { 188 return mStreamCommon->getChannelMask(); 189} 190 191Return<void> StreamOut::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) { 192 return mStreamCommon->getSupportedChannelMasks(_hidl_cb); 193} 194 195Return<Result> StreamOut::setChannelMask(AudioChannelMask mask) { 196 return mStreamCommon->setChannelMask(mask); 197} 198 199Return<AudioFormat> StreamOut::getFormat() { 200 return mStreamCommon->getFormat(); 201} 202 203Return<void> StreamOut::getSupportedFormats(getSupportedFormats_cb _hidl_cb) { 204 return mStreamCommon->getSupportedFormats(_hidl_cb); 205} 206 207Return<Result> StreamOut::setFormat(AudioFormat format) { 208 return mStreamCommon->setFormat(format); 209} 210 211Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) { 212 return mStreamCommon->getAudioProperties(_hidl_cb); 213} 214 215Return<Result> StreamOut::addEffect(uint64_t effectId) { 216 return mStreamCommon->addEffect(effectId); 217} 218 219Return<Result> StreamOut::removeEffect(uint64_t effectId) { 220 return mStreamCommon->removeEffect(effectId); 221} 222 223Return<Result> StreamOut::standby() { 224 return mStreamCommon->standby(); 225} 226 227Return<AudioDevice> StreamOut::getDevice() { 228 return mStreamCommon->getDevice(); 229} 230 231Return<Result> StreamOut::setDevice(const DeviceAddress& address) { 232 return mStreamCommon->setDevice(address); 233} 234 235Return<Result> StreamOut::setConnectedState(const DeviceAddress& address, bool connected) { 236 return mStreamCommon->setConnectedState(address, connected); 237} 238 239Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) { 240 return mStreamCommon->setHwAvSync(hwAvSync); 241} 242 243Return<void> StreamOut::getParameters( 244 const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) { 245 return mStreamCommon->getParameters(keys, _hidl_cb); 246} 247 248Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& parameters) { 249 return mStreamCommon->setParameters(parameters); 250} 251 252Return<void> StreamOut::debugDump(const hidl_handle& fd) { 253 return mStreamCommon->debugDump(fd); 254} 255 256Return<Result> StreamOut::close() { 257 if (mIsClosed) return Result::INVALID_STATE; 258 mIsClosed = true; 259 if (mWriteThread.get()) { 260 mStopWriteThread.store(true, std::memory_order_release); 261 } 262 if (mEfGroup) { 263 mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY)); 264 } 265 return Result::OK; 266} 267 268// Methods from ::android::hardware::audio::V2_0::IStreamOut follow. 269Return<uint32_t> StreamOut::getLatency() { 270 return mStream->get_latency(mStream); 271} 272 273Return<Result> StreamOut::setVolume(float left, float right) { 274 Result retval(Result::NOT_SUPPORTED); 275 if (mStream->set_volume != NULL) { 276 retval = Stream::analyzeStatus( 277 "set_volume", mStream->set_volume(mStream, left, right)); 278 } 279 return retval; 280} 281 282Return<void> StreamOut::prepareForWriting( 283 uint32_t frameSize, uint32_t framesCount, prepareForWriting_cb _hidl_cb) { 284 status_t status; 285 ThreadInfo threadInfo = { 0, 0 }; 286 // Create message queues. 287 if (mDataMQ) { 288 ALOGE("the client attempts to call prepareForWriting twice"); 289 _hidl_cb(Result::INVALID_STATE, 290 CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(), threadInfo); 291 return Void(); 292 } 293 std::unique_ptr<CommandMQ> tempCommandMQ(new CommandMQ(1)); 294 std::unique_ptr<DataMQ> tempDataMQ( 295 new DataMQ(frameSize * framesCount, true /* EventFlag */)); 296 std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1)); 297 if (!tempCommandMQ->isValid() || !tempDataMQ->isValid() || !tempStatusMQ->isValid()) { 298 ALOGE_IF(!tempCommandMQ->isValid(), "command MQ is invalid"); 299 ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid"); 300 ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid"); 301 _hidl_cb(Result::INVALID_ARGUMENTS, 302 CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(), threadInfo); 303 return Void(); 304 } 305 status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup); 306 if (status != OK || !mEfGroup) { 307 ALOGE("failed creating event flag for data MQ: %s", strerror(-status)); 308 _hidl_cb(Result::INVALID_ARGUMENTS, 309 CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(), threadInfo); 310 return Void(); 311 } 312 313 // Create and launch the thread. 314 mWriteThread = new WriteThread( 315 &mStopWriteThread, 316 mStream, 317 tempCommandMQ.get(), 318 tempDataMQ.get(), 319 tempStatusMQ.get(), 320 mEfGroup); 321 status = mWriteThread->run("writer", PRIORITY_URGENT_AUDIO); 322 if (status != OK) { 323 ALOGW("failed to start writer thread: %s", strerror(-status)); 324 _hidl_cb(Result::INVALID_ARGUMENTS, 325 CommandMQ::Descriptor(), DataMQ::Descriptor(), StatusMQ::Descriptor(), threadInfo); 326 return Void(); 327 } 328 329 mCommandMQ = std::move(tempCommandMQ); 330 mDataMQ = std::move(tempDataMQ); 331 mStatusMQ = std::move(tempStatusMQ); 332 threadInfo.pid = getpid(); 333 threadInfo.tid = mWriteThread->getTid(); 334 _hidl_cb(Result::OK, 335 *mCommandMQ->getDesc(), *mDataMQ->getDesc(), *mStatusMQ->getDesc(), 336 threadInfo); 337 return Void(); 338} 339 340Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) { 341 uint32_t halDspFrames; 342 Result retval = Stream::analyzeStatus( 343 "get_render_position", mStream->get_render_position(mStream, &halDspFrames)); 344 _hidl_cb(retval, halDspFrames); 345 return Void(); 346} 347 348Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) { 349 Result retval(Result::NOT_SUPPORTED); 350 int64_t timestampUs = 0; 351 if (mStream->get_next_write_timestamp != NULL) { 352 retval = Stream::analyzeStatus( 353 "get_next_write_timestamp", 354 mStream->get_next_write_timestamp(mStream, ×tampUs)); 355 } 356 _hidl_cb(retval, timestampUs); 357 return Void(); 358} 359 360Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) { 361 if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED; 362 int result = mStream->set_callback(mStream, StreamOut::asyncCallback, this); 363 if (result == 0) { 364 mCallback = callback; 365 } 366 return Stream::analyzeStatus("set_callback", result); 367} 368 369Return<Result> StreamOut::clearCallback() { 370 if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED; 371 mCallback.clear(); 372 return Result::OK; 373} 374 375// static 376int StreamOut::asyncCallback(stream_callback_event_t event, void*, void *cookie) { 377 wp<StreamOut> weakSelf(reinterpret_cast<StreamOut*>(cookie)); 378 sp<StreamOut> self = weakSelf.promote(); 379 if (self == nullptr || self->mCallback == nullptr) return 0; 380 ALOGV("asyncCallback() event %d", event); 381 switch (event) { 382 case STREAM_CBK_EVENT_WRITE_READY: 383 self->mCallback->onWriteReady(); 384 break; 385 case STREAM_CBK_EVENT_DRAIN_READY: 386 self->mCallback->onDrainReady(); 387 break; 388 case STREAM_CBK_EVENT_ERROR: 389 self->mCallback->onError(); 390 break; 391 default: 392 ALOGW("asyncCallback() unknown event %d", event); 393 break; 394 } 395 return 0; 396} 397 398Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) { 399 _hidl_cb(mStream->pause != NULL, mStream->resume != NULL); 400 return Void(); 401} 402 403Return<Result> StreamOut::pause() { 404 return mStream->pause != NULL ? 405 Stream::analyzeStatus("pause", mStream->pause(mStream)) : 406 Result::NOT_SUPPORTED; 407} 408 409Return<Result> StreamOut::resume() { 410 return mStream->resume != NULL ? 411 Stream::analyzeStatus("resume", mStream->resume(mStream)) : 412 Result::NOT_SUPPORTED; 413} 414 415Return<bool> StreamOut::supportsDrain() { 416 return mStream->drain != NULL; 417} 418 419Return<Result> StreamOut::drain(AudioDrain type) { 420 return mStream->drain != NULL ? 421 Stream::analyzeStatus( 422 "drain", mStream->drain(mStream, static_cast<audio_drain_type_t>(type))) : 423 Result::NOT_SUPPORTED; 424} 425 426Return<Result> StreamOut::flush() { 427 return mStream->flush != NULL ? 428 Stream::analyzeStatus("flush", mStream->flush(mStream)) : 429 Result::NOT_SUPPORTED; 430} 431 432// static 433Result StreamOut::getPresentationPositionImpl( 434 audio_stream_out_t *stream, uint64_t *frames, TimeSpec *timeStamp) { 435 Result retval(Result::NOT_SUPPORTED); 436 if (stream->get_presentation_position == NULL) return retval; 437 struct timespec halTimeStamp; 438 retval = Stream::analyzeStatus( 439 "get_presentation_position", 440 stream->get_presentation_position(stream, frames, &halTimeStamp), 441 // Don't logspam on EINVAL--it's normal for get_presentation_position 442 // to return it sometimes. EAGAIN may be returned by A2DP audio HAL 443 // implementation. 444 EINVAL, EAGAIN); 445 if (retval == Result::OK) { 446 timeStamp->tvSec = halTimeStamp.tv_sec; 447 timeStamp->tvNSec = halTimeStamp.tv_nsec; 448 } 449 return retval; 450} 451 452Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) { 453 uint64_t frames = 0; 454 TimeSpec timeStamp = { 0, 0 }; 455 Result retval = getPresentationPositionImpl(mStream, &frames, &timeStamp); 456 _hidl_cb(retval, frames, timeStamp); 457 return Void(); 458} 459 460Return<Result> StreamOut::start() { 461 return mStreamMmap->start(); 462} 463 464Return<Result> StreamOut::stop() { 465 return mStreamMmap->stop(); 466} 467 468Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) { 469 return mStreamMmap->createMmapBuffer( 470 minSizeFrames, audio_stream_out_frame_size(mStream), _hidl_cb); 471} 472 473Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) { 474 return mStreamMmap->getMmapPosition(_hidl_cb); 475} 476 477} // namespace implementation 478} // namespace V2_0 479} // namespace audio 480} // namespace hardware 481} // namespace android 482