blob: 4bb22742db9f5d537174314c089f2e4e43ad2ff2 [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();
91 Result retval = Result::OK;
92 uint64_t written = 0;
93 if (mDataMQ->read(&mBuffer[0], availToRead)) {
94 ssize_t writeResult = mStream->write(mStream, &mBuffer[0], availToRead);
95 if (writeResult >= 0) {
96 written = writeResult;
97 } else {
98 retval = Stream::analyzeStatus("write", writeResult);
99 }
100 }
101 uint64_t frames = 0;
102 struct timespec halTimeStamp = { 0, 0 };
103 if (retval == Result::OK && mStream->get_presentation_position != NULL) {
104 mStream->get_presentation_position(mStream, &frames, &halTimeStamp);
105 }
106 IStreamOut::WriteStatus status = { retval, written, frames,
107 { static_cast<uint64_t>(halTimeStamp.tv_sec),
108 static_cast<uint64_t>(halTimeStamp.tv_nsec) } };
109 if (!mStatusMQ->write(&status)) {
110 ALOGW("status message queue write failed");
111 }
112 mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::NOT_FULL));
113 }
114
115 return false;
116}
117
118} // namespace
119
Mikhail Naganov10548292016-10-31 10:39:47 -0700120StreamOut::StreamOut(audio_hw_device_t* device, audio_stream_out_t* stream)
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800121 : mIsClosed(false), mDevice(device), mStream(stream),
Eric Laurent7deb7da2016-12-15 19:15:45 -0800122 mStreamCommon(new Stream(&stream->common)),
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800123 mStreamMmap(new StreamMmap<audio_stream_out_t>(stream)),
124 mEfGroup(nullptr), mStopWriteThread(false) {
Mikhail Naganov10548292016-10-31 10:39:47 -0700125}
126
127StreamOut::~StreamOut() {
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800128 close();
Mikhail Naganov10548292016-10-31 10:39:47 -0700129 mStream = nullptr;
130 mDevice = nullptr;
131}
132
133// Methods from ::android::hardware::audio::V2_0::IStream follow.
134Return<uint64_t> StreamOut::getFrameSize() {
135 return audio_stream_out_frame_size(mStream);
136}
137
138Return<uint64_t> StreamOut::getFrameCount() {
139 return mStreamCommon->getFrameCount();
140}
141
142Return<uint64_t> StreamOut::getBufferSize() {
143 return mStreamCommon->getBufferSize();
144}
145
146Return<uint32_t> StreamOut::getSampleRate() {
147 return mStreamCommon->getSampleRate();
148}
149
150Return<void> StreamOut::getSupportedSampleRates(getSupportedSampleRates_cb _hidl_cb) {
151 return mStreamCommon->getSupportedSampleRates(_hidl_cb);
152}
153
154Return<Result> StreamOut::setSampleRate(uint32_t sampleRateHz) {
155 return mStreamCommon->setSampleRate(sampleRateHz);
156}
157
158Return<AudioChannelMask> StreamOut::getChannelMask() {
159 return mStreamCommon->getChannelMask();
160}
161
162Return<void> StreamOut::getSupportedChannelMasks(getSupportedChannelMasks_cb _hidl_cb) {
163 return mStreamCommon->getSupportedChannelMasks(_hidl_cb);
164}
165
166Return<Result> StreamOut::setChannelMask(AudioChannelMask mask) {
167 return mStreamCommon->setChannelMask(mask);
168}
169
170Return<AudioFormat> StreamOut::getFormat() {
171 return mStreamCommon->getFormat();
172}
173
174Return<void> StreamOut::getSupportedFormats(getSupportedFormats_cb _hidl_cb) {
175 return mStreamCommon->getSupportedFormats(_hidl_cb);
176}
177
178Return<Result> StreamOut::setFormat(AudioFormat format) {
179 return mStreamCommon->setFormat(format);
180}
181
182Return<void> StreamOut::getAudioProperties(getAudioProperties_cb _hidl_cb) {
183 return mStreamCommon->getAudioProperties(_hidl_cb);
184}
185
186Return<Result> StreamOut::addEffect(uint64_t effectId) {
187 return mStreamCommon->addEffect(effectId);
188}
189
190Return<Result> StreamOut::removeEffect(uint64_t effectId) {
191 return mStreamCommon->removeEffect(effectId);
192}
193
194Return<Result> StreamOut::standby() {
195 return mStreamCommon->standby();
196}
197
198Return<AudioDevice> StreamOut::getDevice() {
199 return mStreamCommon->getDevice();
200}
201
202Return<Result> StreamOut::setDevice(const DeviceAddress& address) {
203 return mStreamCommon->setDevice(address);
204}
205
206Return<Result> StreamOut::setConnectedState(const DeviceAddress& address, bool connected) {
207 return mStreamCommon->setConnectedState(address, connected);
208}
209
210Return<Result> StreamOut::setHwAvSync(uint32_t hwAvSync) {
211 return mStreamCommon->setHwAvSync(hwAvSync);
212}
213
214Return<void> StreamOut::getParameters(
215 const hidl_vec<hidl_string>& keys, getParameters_cb _hidl_cb) {
216 return mStreamCommon->getParameters(keys, _hidl_cb);
217}
218
219Return<Result> StreamOut::setParameters(const hidl_vec<ParameterValue>& parameters) {
220 return mStreamCommon->setParameters(parameters);
221}
222
Martijn Coenen70b9a152016-11-18 15:29:32 +0100223Return<void> StreamOut::debugDump(const hidl_handle& fd) {
Mikhail Naganov10548292016-10-31 10:39:47 -0700224 return mStreamCommon->debugDump(fd);
225}
226
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800227Return<Result> StreamOut::close() {
228 if (mIsClosed) return Result::INVALID_STATE;
229 mIsClosed = true;
230 if (mWriteThread.get()) {
231 mStopWriteThread.store(true, std::memory_order_release);
232 status_t status = mWriteThread->requestExitAndWait();
233 ALOGE_IF(status, "write thread exit error: %s", strerror(-status));
234 }
235 if (mEfGroup) {
236 status_t status = EventFlag::deleteEventFlag(&mEfGroup);
237 ALOGE_IF(status, "write MQ event flag deletion error: %s", strerror(-status));
238 }
239 mCallback.clear();
240 mDevice->close_output_stream(mDevice, mStream);
241 return Result::OK;
242}
243
Mikhail Naganov10548292016-10-31 10:39:47 -0700244// Methods from ::android::hardware::audio::V2_0::IStreamOut follow.
245Return<uint32_t> StreamOut::getLatency() {
246 return mStream->get_latency(mStream);
247}
248
249Return<Result> StreamOut::setVolume(float left, float right) {
250 Result retval(Result::NOT_SUPPORTED);
251 if (mStream->set_volume != NULL) {
Eric Laurent7deb7da2016-12-15 19:15:45 -0800252 retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700253 "set_volume", mStream->set_volume(mStream, left, right));
254 }
255 return retval;
256}
257
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800258Return<void> StreamOut::prepareForWriting(
259 uint32_t frameSize, uint32_t framesCount, ThreadPriority threadPriority,
260 prepareForWriting_cb _hidl_cb) {
261 status_t status;
262 // Create message queues.
263 if (mDataMQ) {
264 ALOGE("the client attempts to call prepareForWriting twice");
265 _hidl_cb(Result::INVALID_STATE,
266 MQDescriptorSync<uint8_t>(), MQDescriptorSync<WriteStatus>());
267 return Void();
Mikhail Naganov10548292016-10-31 10:39:47 -0700268 }
Mikhail Naganovb29438e2016-12-22 09:21:34 -0800269 std::unique_ptr<DataMQ> tempDataMQ(
270 new DataMQ(frameSize * framesCount, true /* EventFlag */));
271 std::unique_ptr<StatusMQ> tempStatusMQ(new StatusMQ(1));
272 if (!tempDataMQ->isValid() || !tempStatusMQ->isValid()) {
273 ALOGE_IF(!tempDataMQ->isValid(), "data MQ is invalid");
274 ALOGE_IF(!tempStatusMQ->isValid(), "status MQ is invalid");
275 _hidl_cb(Result::INVALID_ARGUMENTS,
276 MQDescriptorSync<uint8_t>(), MQDescriptorSync<WriteStatus>());
277 return Void();
278 }
279 // TODO: Remove event flag management once blocking MQ is implemented. b/33815422
280 status = EventFlag::createEventFlag(tempDataMQ->getEventFlagWord(), &mEfGroup);
281 if (status != OK || !mEfGroup) {
282 ALOGE("failed creating event flag for data MQ: %s", strerror(-status));
283 _hidl_cb(Result::INVALID_ARGUMENTS,
284 MQDescriptorSync<uint8_t>(), MQDescriptorSync<WriteStatus>());
285 return Void();
286 }
287
288 // Create and launch the thread.
289 mWriteThread = new WriteThread(
290 &mStopWriteThread,
291 mStream,
292 tempDataMQ.get(),
293 tempStatusMQ.get(),
294 mEfGroup,
295 threadPriority);
296 status = mWriteThread->run("writer", PRIORITY_URGENT_AUDIO);
297 if (status != OK) {
298 ALOGW("failed to start writer thread: %s", strerror(-status));
299 _hidl_cb(Result::INVALID_ARGUMENTS,
300 MQDescriptorSync<uint8_t>(), MQDescriptorSync<WriteStatus>());
301 return Void();
302 }
303
304 mDataMQ = std::move(tempDataMQ);
305 mStatusMQ = std::move(tempStatusMQ);
306 _hidl_cb(Result::OK, *mDataMQ->getDesc(), *mStatusMQ->getDesc());
Mikhail Naganov10548292016-10-31 10:39:47 -0700307 return Void();
308}
309
310Return<void> StreamOut::getRenderPosition(getRenderPosition_cb _hidl_cb) {
311 uint32_t halDspFrames;
Eric Laurent7deb7da2016-12-15 19:15:45 -0800312 Result retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700313 "get_render_position", mStream->get_render_position(mStream, &halDspFrames));
314 _hidl_cb(retval, halDspFrames);
315 return Void();
316}
317
318Return<void> StreamOut::getNextWriteTimestamp(getNextWriteTimestamp_cb _hidl_cb) {
319 Result retval(Result::NOT_SUPPORTED);
320 int64_t timestampUs = 0;
321 if (mStream->get_next_write_timestamp != NULL) {
Eric Laurent7deb7da2016-12-15 19:15:45 -0800322 retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700323 "get_next_write_timestamp",
324 mStream->get_next_write_timestamp(mStream, &timestampUs));
325 }
326 _hidl_cb(retval, timestampUs);
327 return Void();
328}
329
330Return<Result> StreamOut::setCallback(const sp<IStreamOutCallback>& callback) {
331 if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
332 int result = mStream->set_callback(mStream, StreamOut::asyncCallback, this);
333 if (result == 0) {
334 mCallback = callback;
335 }
Eric Laurent7deb7da2016-12-15 19:15:45 -0800336 return Stream::analyzeStatus("set_callback", result);
Mikhail Naganov10548292016-10-31 10:39:47 -0700337}
338
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800339Return<Result> StreamOut::clearCallback() {
340 if (mStream->set_callback == NULL) return Result::NOT_SUPPORTED;
341 mCallback.clear();
342 return Result::OK;
343}
344
Mikhail Naganov10548292016-10-31 10:39:47 -0700345// static
346int StreamOut::asyncCallback(stream_callback_event_t event, void*, void *cookie) {
347 wp<StreamOut> weakSelf(reinterpret_cast<StreamOut*>(cookie));
348 sp<StreamOut> self = weakSelf.promote();
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800349 if (self == nullptr || self->mCallback == nullptr) return 0;
Mikhail Naganov10548292016-10-31 10:39:47 -0700350 ALOGV("asyncCallback() event %d", event);
351 switch (event) {
352 case STREAM_CBK_EVENT_WRITE_READY:
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800353 self->mCallback->onWriteReady();
Mikhail Naganov10548292016-10-31 10:39:47 -0700354 break;
355 case STREAM_CBK_EVENT_DRAIN_READY:
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800356 self->mCallback->onDrainReady();
Mikhail Naganov10548292016-10-31 10:39:47 -0700357 break;
358 case STREAM_CBK_EVENT_ERROR:
Mikhail Naganov6e81e9b2016-11-16 16:30:17 -0800359 self->mCallback->onError();
Mikhail Naganov10548292016-10-31 10:39:47 -0700360 break;
361 default:
362 ALOGW("asyncCallback() unknown event %d", event);
363 break;
364 }
365 return 0;
366}
367
368Return<void> StreamOut::supportsPauseAndResume(supportsPauseAndResume_cb _hidl_cb) {
369 _hidl_cb(mStream->pause != NULL, mStream->resume != NULL);
370 return Void();
371}
372
373Return<Result> StreamOut::pause() {
374 return mStream->pause != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800375 Stream::analyzeStatus("pause", mStream->pause(mStream)) :
Mikhail Naganov10548292016-10-31 10:39:47 -0700376 Result::NOT_SUPPORTED;
377}
378
379Return<Result> StreamOut::resume() {
380 return mStream->resume != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800381 Stream::analyzeStatus("resume", mStream->resume(mStream)) :
Mikhail Naganov10548292016-10-31 10:39:47 -0700382 Result::NOT_SUPPORTED;
383}
384
385Return<bool> StreamOut::supportsDrain() {
386 return mStream->drain != NULL;
387}
388
389Return<Result> StreamOut::drain(AudioDrain type) {
390 return mStream->drain != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800391 Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700392 "drain", mStream->drain(mStream, static_cast<audio_drain_type_t>(type))) :
393 Result::NOT_SUPPORTED;
394}
395
396Return<Result> StreamOut::flush() {
397 return mStream->flush != NULL ?
Eric Laurent7deb7da2016-12-15 19:15:45 -0800398 Stream::analyzeStatus("flush", mStream->flush(mStream)) :
Mikhail Naganov10548292016-10-31 10:39:47 -0700399 Result::NOT_SUPPORTED;
400}
401
402Return<void> StreamOut::getPresentationPosition(getPresentationPosition_cb _hidl_cb) {
403 Result retval(Result::NOT_SUPPORTED);
404 uint64_t frames = 0;
405 TimeSpec timeStamp = { 0, 0 };
406 if (mStream->get_presentation_position != NULL) {
407 struct timespec halTimeStamp;
Eric Laurent7deb7da2016-12-15 19:15:45 -0800408 retval = Stream::analyzeStatus(
Mikhail Naganov10548292016-10-31 10:39:47 -0700409 "get_presentation_position",
Mikhail Naganov13f43f42016-12-07 17:05:40 -0800410 mStream->get_presentation_position(mStream, &frames, &halTimeStamp),
411 // Don't logspam on EINVAL--it's normal for get_presentation_position
412 // to return it sometimes.
413 EINVAL);
Mikhail Naganov10548292016-10-31 10:39:47 -0700414 if (retval == Result::OK) {
415 timeStamp.tvSec = halTimeStamp.tv_sec;
416 timeStamp.tvNSec = halTimeStamp.tv_nsec;
417 }
418 }
419 _hidl_cb(retval, frames, timeStamp);
420 return Void();
421}
422
Eric Laurent7deb7da2016-12-15 19:15:45 -0800423Return<Result> StreamOut::start() {
424 return mStreamMmap->start();
425}
426
427Return<Result> StreamOut::stop() {
428 return mStreamMmap->stop();
429}
430
431Return<void> StreamOut::createMmapBuffer(int32_t minSizeFrames, createMmapBuffer_cb _hidl_cb) {
432 return mStreamMmap->createMmapBuffer(
433 minSizeFrames, audio_stream_out_frame_size(mStream), _hidl_cb);
434}
435
436Return<void> StreamOut::getMmapPosition(getMmapPosition_cb _hidl_cb) {
437 return mStreamMmap->getMmapPosition(_hidl_cb);
438}
439
Mikhail Naganov10548292016-10-31 10:39:47 -0700440} // namespace implementation
441} // namespace V2_0
442} // namespace audio
443} // namespace hardware
444} // namespace android