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