blob: 1948b68e2f844509d61f958a1fb201b01f26018e [file] [log] [blame]
Mikhail Naganov10548292016-10-31 10:39:47 -07001/*
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"
Mikhail Naganov685f0e32016-12-16 17:18:08 -080018//#define LOG_NDEBUG 0
Mikhail Naganov10548292016-10-31 10:39:47 -070019
Yifan Hongf9d30342016-11-30 13:45:34 -080020#include <android/log.h>
Mikhail Naganovb29438e2016-12-22 09:21:34 -080021#include <hardware/audio.h>
22#include <mediautils/SchedulingPolicyService.h>
Mikhail Naganov10548292016-10-31 10:39:47 -070023
24#include "StreamOut.h"
25
26namespace android {
27namespace hardware {
28namespace audio {
29namespace V2_0 {
30namespace implementation {
31
Mikhail Naganovb29438e2016-12-22 09:21:34 -080032namespace {
33
34class WriteThread : public Thread {
35 public:
36 // WriteThread's lifespan never exceeds StreamOut's lifespan.
37 WriteThread(std::atomic<bool>* stop,
38 audio_stream_out_t* stream,
39 StreamOut::DataMQ* dataMQ,
40 StreamOut::StatusMQ* statusMQ,
41 EventFlag* efGroup,
42 ThreadPriority threadPriority)
43 : Thread(false /*canCallJava*/),
44 mStop(stop),
45 mStream(stream),
46 mDataMQ(dataMQ),
47 mStatusMQ(statusMQ),
48 mEfGroup(efGroup),
49 mThreadPriority(threadPriority),
50 mBuffer(new uint8_t[dataMQ->getQuantumCount()]) {
51 }
52 virtual ~WriteThread() {}
53
54 status_t readyToRun() override;
55
56 private:
57 std::atomic<bool>* mStop;
58 audio_stream_out_t* mStream;
59 StreamOut::DataMQ* mDataMQ;
60 StreamOut::StatusMQ* mStatusMQ;
61 EventFlag* mEfGroup;
62 ThreadPriority mThreadPriority;
63 std::unique_ptr<uint8_t[]> mBuffer;
64
65 bool threadLoop() override;
66};
67
68status_t WriteThread::readyToRun() {
69 if (mThreadPriority != ThreadPriority::NORMAL) {
70 int err = requestPriority(
71 getpid(), getTid(), static_cast<int>(mThreadPriority), true /*asynchronous*/);
72 ALOGW_IF(err, "failed to set priority %d for pid %d tid %d; error %d",
73 static_cast<int>(mThreadPriority), getpid(), getTid(), err);
74 }
75 return OK;
76}
77
78bool WriteThread::threadLoop() {
79 // This implementation doesn't return control back to the Thread until it decides to stop,
80 // as the Thread uses mutexes, and this can lead to priority inversion.
81 while(!std::atomic_load_explicit(mStop, std::memory_order_acquire)) {
82 // TODO: Remove manual event flag handling once blocking MQ is implemented. b/33815422
83 uint32_t efState = 0;
84 mEfGroup->wait(
85 static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY), &efState, NS_PER_SEC);
86 if (!(efState & static_cast<uint32_t>(MessageQueueFlagBits::NOT_EMPTY))) {
87 continue; // Nothing to do.
88 }
89
90 const size_t availToRead = mDataMQ->availableToRead();
Mikhail Naganovee901e32017-01-12 09:28:52 -080091 IStreamOut::WriteStatus status;
92 status.writeRetval = Result::OK;
93 status.written = 0;
Mikhail Naganovb29438e2016-12-22 09:21:34 -080094 if (mDataMQ->read(&mBuffer[0], availToRead)) {
95 ssize_t writeResult = mStream->write(mStream, &mBuffer[0], availToRead);
96 if (writeResult >= 0) {
Mikhail Naganovee901e32017-01-12 09:28:52 -080097 status.written = writeResult;
Mikhail Naganovb29438e2016-12-22 09:21:34 -080098 } else {
Mikhail Naganovee901e32017-01-12 09:28:52 -080099 status.writeRetval = Stream::analyzeStatus("write", writeResult);
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800100 }
101 }
Mikhail Naganovee901e32017-01-12 09:28:52 -0800102 status.presentationPositionRetval = status.writeRetval == Result::OK ?
103 StreamOut::getPresentationPositionImpl(mStream, &status.frames, &status.timeStamp) :
104 Result::OK;
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800105 if (!mStatusMQ->write(&status)) {
106 ALOGW("status message queue write failed");
107 }
108 mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
109 }
110
111 return false;
112}
113
114} // namespace
115
Mikhail Naganov10548292016-10-31 10:39:47 -0700116StreamOut::StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream)
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800117 : mIsClosed(false), mDevice(device), mStream(stream),
Eric Laurent7deb7da2016-12-15 19:15:45 -0800118 mStreamCommon(new Stream(&stream->common)),
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800119 mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
120 mEfGroup(nullptr), mStopWriteThread(false) {
Mikhail Naganov10548292016-10-31 10:39:47 -0700121}
122
123StreamOut::~StreamOut() {
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800124 close();
Mikhail Naganov10548292016-10-31 10:39:47 -0700125 mStream = nullptr;
126 mDevice = nullptr;
127}
128
129// Methods from ::android::hardware::audio::V2_0::IStream follow.
130Return<uint64_t> StreamOut::getFrameSize() {
131 return audio_stream_out_frame_size(mStream);
132}
133
134Return<uint64_t> StreamOut::getFrameCount() {
135 return mStreamCommon->getFrameCount();
136}
137
138Return<uint64_t> StreamOut::getBufferSize() {
139 return mStreamCommon->getBufferSize();
140}
141
142Return<uint32_t> StreamOut::getSampleRate() {
143 return mStreamCommon->getSampleRate();
144}
145
146Return<void> StreamOut::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
147 return mStreamCommon->getSupportedSampleRates(_hidl_cb);
148}
149
150Return<Result> StreamOut::setSampleRate(uint32_t sampleRateHz) {
151 return mStreamCommon->setSampleRate(sampleRateHz);
152}
153
154Return<AudioChannelMask> StreamOut::getChannelMask() {
155 return mStreamCommon->getChannelMask();
156}
157
158Return<void> StreamOut::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
159 return mStreamCommon->getSupportedChannelMasks(_hidl_cb);
160}
161
162Return<Result> StreamOut::setChannelMask(AudioChannelMask mask) {
163 return mStreamCommon->setChannelMask(mask);
164}
165
166Return<AudioFormat> StreamOut::getFormat() {
167 return mStreamCommon->getFormat();
168}
169
170Return<void> StreamOut::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
171 return mStreamCommon->getSupportedFormats(_hidl_cb);
172}
173
174Return<Result> StreamOut::setFormat(AudioFormat format) {
175 return mStreamCommon->setFormat(format);
176}
177
178Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
179 return mStreamCommon->getAudioProperties(_hidl_cb);
180}
181
182Return<Result> StreamOut::addEffect(uint64_t effectId) {
183 return mStreamCommon->addEffect(effectId);
184}
185
186Return<Result> StreamOut::removeEffect(uint64_t effectId) {
187 return mStreamCommon->removeEffect(effectId);
188}
189
190Return<Result> StreamOut::standby() {
191 return mStreamCommon->standby();
192}
193
194Return<AudioDevice> StreamOut::getDevice() {
195 return mStreamCommon->getDevice();
196}
197
198Return<Result> StreamOut::setDevice(const DeviceAddress& address) {
199 return mStreamCommon->setDevice(address);
200}
201
202Return<Result> StreamOut::setConnectedState(const DeviceAddress& address, bool connected) {
203 return mStreamCommon->setConnectedState(address, connected);
204}
205
206Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) {
207 return mStreamCommon->setHwAvSync(hwAvSync);
208}
209
210Return<void> StreamOut::getParameters(
211 const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
212 return mStreamCommon->getParameters(keys, _hidl_cb);
213}
214
215Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& parameters) {
216 return mStreamCommon->setParameters(parameters);
217}
218
Martijn Coenen70b9a152016-11-18 15:29:32 +0100219Return<void> StreamOut::debugDump(const hidl_handle& fd) {
Mikhail Naganov10548292016-10-31 10:39:47 -0700220 return mStreamCommon->debugDump(fd);
221}
222
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800223Return<Result> StreamOut::close() {
224 if (mIsClosed) return Result::INVALID_STATE;
225 mIsClosed = true;
226 if (mWriteThread.get()) {
227 mStopWriteThread.store(true, std::memory_order_release);
228 status_t status = mWriteThread->requestExitAndWait();
229 ALOGE_IF(status, "write thread exit error: %s", strerror(-status));
230 }
231 if (mEfGroup) {
232 status_t status = EventFlag::deleteEventFlag(&mEfGroup);
233 ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
234 }
235 mCallback.clear();
236 mDevice->close_output_stream(mDevice, mStream);
237 return Result::OK;
238}
239
Mikhail Naganov10548292016-10-31 10:39:47 -0700240// Methods from ::android::hardware::audio::V2_0::IStreamOut follow.
241Return<uint32_t> StreamOut::getLatency() {
242 return mStream->get_latency(mStream);
243}
244
245Return<Result> StreamOut::setVolume(float left, float right) {
246 Result retval(Result::NOT_SUPPORTED);
247 if (mStream->set_volume != NULL) {
Eric Laurent7deb7da2016-12-15 19:15:45 -0800248 retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700249 "set_volume", mStream->set_volume(mStream, left, right));
250 }
251 return retval;
252}
253
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800254Return<void> StreamOut::prepareForWriting(
255 uint32_t frameSize, uint32_t framesCount, ThreadPriority threadPriority,
256 prepareForWriting_cb _hidl_cb) {
257 status_t status;
258 // Create message queues.
259 if (mDataMQ) {
260 ALOGE("the client attempts to call prepareForWriting twice");
261 _hidl_cb(Result::INVALID_STATE,
Hridya Valsaraju790db102017-01-10 08:58:23 -0800262 DataMQ::Descriptor(), StatusMQ::Descriptor());
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800263 return Void();
Mikhail Naganov10548292016-10-31 10:39:47 -0700264 }
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800265 std::unique_ptr<DataMQ> tempDataMQ(
266 new DataMQ(frameSize * framesCount, true /* EventFlag */));
267 std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));
268 if (!tempDataMQ->isValid() || !tempStatusMQ->isValid()) {
269 ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");
270 ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");
271 _hidl_cb(Result::INVALID_ARGUMENTS,
Hridya Valsaraju790db102017-01-10 08:58:23 -0800272 DataMQ::Descriptor(), StatusMQ::Descriptor());
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800273 return Void();
274 }
275 // TODO: Remove event flag management once blocking MQ is implemented. b/33815422
276 status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
277 if (status != OK || !mEfGroup) {
278 ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
279 _hidl_cb(Result::INVALID_ARGUMENTS,
Hridya Valsaraju790db102017-01-10 08:58:23 -0800280 DataMQ::Descriptor(), StatusMQ::Descriptor());
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800281 return Void();
282 }
283
284 // Create and launch the thread.
285 mWriteThread = new WriteThread(
286 &mStopWriteThread,
287 mStream,
288 tempDataMQ.get(),
289 tempStatusMQ.get(),
290 mEfGroup,
291 threadPriority);
292 status = mWriteThread->run("writer", PRIORITY_URGENT_AUDIO);
293 if (status != OK) {
294 ALOGW("failed to start writer thread: %s", strerror(-status));
295 _hidl_cb(Result::INVALID_ARGUMENTS,
Hridya Valsaraju790db102017-01-10 08:58:23 -0800296 DataMQ::Descriptor(), StatusMQ::Descriptor());
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800297 return Void();
298 }
299
300 mDataMQ = std::move(tempDataMQ);
301 mStatusMQ = std::move(tempStatusMQ);
302 _hidl_cb(Result::OK, *mDataMQ->getDesc(), *mStatusMQ->getDesc());
Mikhail Naganov10548292016-10-31 10:39:47 -0700303 return Void();
304}
305
306Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) {
307 uint32_t halDspFrames;
Eric Laurent7deb7da2016-12-15 19:15:45 -0800308 Result retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700309 "get_render_position", mStream->get_render_position(mStream, &halDspFrames));
310 _hidl_cb(retval, halDspFrames);
311 return Void();
312}
313
314Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) {
315 Result retval(Result::NOT_SUPPORTED);
316 int64_t timestampUs = 0;
317 if (mStream->get_next_write_timestamp != NULL) {
Eric Laurent7deb7da2016-12-15 19:15:45 -0800318 retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700319 "get_next_write_timestamp",
320 mStream->get_next_write_timestamp(mStream, &timestampUs));
321 }
322 _hidl_cb(retval, timestampUs);
323 return Void();
324}
325
326Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
327 if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
328 int result = mStream->set_callback(mStream, StreamOut::asyncCallback, this);
329 if (result == 0) {
330 mCallback = callback;
331 }
Eric Laurent7deb7da2016-12-15 19:15:45 -0800332 return Stream::analyzeStatus("set_callback", result);
Mikhail Naganov10548292016-10-31 10:39:47 -0700333}
334
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800335Return<Result> StreamOut::clearCallback() {
336 if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
337 mCallback.clear();
338 return Result::OK;
339}
340
Mikhail Naganov10548292016-10-31 10:39:47 -0700341// static
342int StreamOut::asyncCallback(stream_callback_event_t event, void*, void *cookie) {
343 wp<StreamOut> weakSelf(reinterpret_cast<StreamOut*>(cookie));
344 sp<StreamOut> self = weakSelf.promote();
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800345 if (self == nullptr || self->mCallback == nullptr) return 0;
Mikhail Naganov10548292016-10-31 10:39:47 -0700346 ALOGV("asyncCallback() event %d", event);
347 switch (event) {
348 case STREAM_CBK_EVENT_WRITE_READY:
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800349 self->mCallback->onWriteReady();
Mikhail Naganov10548292016-10-31 10:39:47 -0700350 break;
351 case STREAM_CBK_EVENT_DRAIN_READY:
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800352 self->mCallback->onDrainReady();
Mikhail Naganov10548292016-10-31 10:39:47 -0700353 break;
354 case STREAM_CBK_EVENT_ERROR:
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800355 self->mCallback->onError();
Mikhail Naganov10548292016-10-31 10:39:47 -0700356 break;
357 default:
358 ALOGW("asyncCallback() unknown event %d", event);
359 break;
360 }
361 return 0;
362}
363
364Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) {
365 _hidl_cb(mStream->pause != NULL, mStream->resume != NULL);
366 return Void();
367}
368
369Return<Result> StreamOut::pause() {
370 return mStream->pause != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800371 Stream::analyzeStatus("pause", mStream->pause(mStream)) :
Mikhail Naganov10548292016-10-31 10:39:47 -0700372 Result::NOT_SUPPORTED;
373}
374
375Return<Result> StreamOut::resume() {
376 return mStream->resume != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800377 Stream::analyzeStatus("resume", mStream->resume(mStream)) :
Mikhail Naganov10548292016-10-31 10:39:47 -0700378 Result::NOT_SUPPORTED;
379}
380
381Return<bool> StreamOut::supportsDrain() {
382 return mStream->drain != NULL;
383}
384
385Return<Result> StreamOut::drain(AudioDrain type) {
386 return mStream->drain != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800387 Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700388 "drain", mStream->drain(mStream, static_cast<audio_drain_type_t>(type))) :
389 Result::NOT_SUPPORTED;
390}
391
392Return<Result> StreamOut::flush() {
393 return mStream->flush != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800394 Stream::analyzeStatus("flush", mStream->flush(mStream)) :
Mikhail Naganov10548292016-10-31 10:39:47 -0700395 Result::NOT_SUPPORTED;
396}
397
Mikhail Naganovee901e32017-01-12 09:28:52 -0800398// static
399Result StreamOut::getPresentationPositionImpl(
400 audio_stream_out_t *stream, uint64_t *frames, TimeSpec *timeStamp) {
Mikhail Naganov10548292016-10-31 10:39:47 -0700401 Result retval(Result::NOT_SUPPORTED);
Mikhail Naganovee901e32017-01-12 09:28:52 -0800402 if (stream->get_presentation_position == NULL) return retval;
403 struct timespec halTimeStamp;
404 retval = Stream::analyzeStatus(
405 "get_presentation_position",
406 stream->get_presentation_position(stream, frames, &halTimeStamp),
407 // Don't logspam on EINVAL--it's normal for get_presentation_position
408 // to return it sometimes.
409 EINVAL);
410 if (retval == Result::OK) {
411 timeStamp->tvSec = halTimeStamp.tv_sec;
412 timeStamp->tvNSec = halTimeStamp.tv_nsec;
413 }
414 return retval;
415}
416
417Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
Mikhail Naganov10548292016-10-31 10:39:47 -0700418 uint64_t frames = 0;
419 TimeSpec timeStamp = { 0, 0 };
Mikhail Naganovee901e32017-01-12 09:28:52 -0800420 Result retval = getPresentationPositionImpl(mStream, &frames, &timeStamp);
Mikhail Naganov10548292016-10-31 10:39:47 -0700421 _hidl_cb(retval, frames, timeStamp);
422 return Void();
423}
424
Eric Laurent7deb7da2016-12-15 19:15:45 -0800425Return<Result> StreamOut::start() {
426 return mStreamMmap->start();
427}
428
429Return<Result> StreamOut::stop() {
430 return mStreamMmap->stop();
431}
432
433Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) {
434 return mStreamMmap->createMmapBuffer(
435 minSizeFrames, audio_stream_out_frame_size(mStream), _hidl_cb);
436}
437
438Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) {
439 return mStreamMmap->getMmapPosition(_hidl_cb);
440}
441
Mikhail Naganov10548292016-10-31 10:39:47 -0700442} // namespace implementation
443} // namespace V2_0
444} // namespace audio
445} // namespace hardware
446} // namespace android