ssize_t A2dpAudioInterface::A2dpAudioStreamOut::write(const void* buffer, size_t bytes) { Mutex::Autolock lock(mLock); size_t remaining = bytes; status_t status = -1; if (!mBluetoothEnabled || mClosing || mSuspended) { LOGV("A2dpAudioStreamOut::write(), but bluetooth disabled \ mBluetoothEnabled %d, mClosing %d, mSuspended %d", mBluetoothEnabled, mClosing, mSuspended); goto Error; } status = init(); if (status < 0) goto Error; while (remaining > 0) { status = a2dp_write(mData, buffer, remaining); if (status <= 0) { LOGE("a2dp_write failed err: %d\n", status); goto Error; } remaining -= status; buffer = ((char *)buffer) + status; } mStandby = false; return bytes; Error: // Simulate audio output timing in case of error usleep(((bytes * 1000 )/ frameSize() / sampleRate()) * 1000); return status; }
static void *_out_buf_thread_func(void *context) { struct astream_out *out = (struct astream_out *)context; pthread_mutex_lock(&out->buf_lock); while(!out->buf_thread_exit) { size_t frames; frames = _out_frames_ready_locked(out); while (frames && !out->buf_thread_exit) { int retries = MAX_WRITE_RETRIES; uint64_t now; uint32_t elapsed_us; while (frames > 0 && !out->buf_thread_exit) { int ret; uint32_t buffer_duration_us; /* PCM format is always 16bit stereo */ size_t bytes = frames * sizeof(uint32_t); if (bytes > out->buffer_size) { bytes = out->buffer_size; } pthread_mutex_lock(&out->lock); if (out->standby) { /* abort and clear all pending frames if standby requested */ pthread_mutex_unlock(&out->lock); frames = _out_frames_ready_locked(out); _out_inc_rd_idx_locked(out, frames); goto wait; } /* indicate to out_standby_stream_locked() that a2dp_write() is active */ out->write_busy = true; pthread_mutex_unlock(&out->lock); pthread_mutex_unlock(&out->buf_lock); ret = a2dp_write(out->data, out->buf + out->buf_rd_idx, bytes); /* clear write_busy condition */ pthread_mutex_lock(&out->buf_lock); pthread_mutex_lock(&out->lock); out->write_busy = false; pthread_cond_signal(&out->write_cond); pthread_mutex_unlock(&out->lock); if (ret < 0) { LOGE("%s: a2dp_write failed (%d)\n", __func__, ret); /* skip pending frames in case of write error */ _out_inc_rd_idx_locked(out, frames); break; } else if (ret == 0) { if (retries-- == 0) { /* skip pending frames in case of multiple time out */ _out_inc_rd_idx_locked(out, frames); break; } continue; } ret /= sizeof(uint32_t); _out_inc_rd_idx_locked(out, ret); frames -= ret; /* XXX: PLEASE FIX ME!!!! */ /* if A2DP sink runs abnormally fast, sleep a little so that * audioflinger mixer thread does no spin and starve other threads. */ /* NOTE: It is likely that the A2DP headset is being disconnected */ now = system_time(); elapsed_us = (now - out->last_write_time) / 1000UL; buffer_duration_us = ((ret * 1000) / out->sample_rate) * 1000; if (elapsed_us < (buffer_duration_us / 4)) { LOGV("A2DP sink runs too fast"); usleep(buffer_duration_us - elapsed_us); } out->last_write_time = now; } frames = _out_frames_ready_locked(out); } wait: if (!out->buf_thread_exit) { pthread_cond_wait(&out->buf_cond, &out->buf_lock); } } pthread_mutex_unlock(&out->buf_lock); return NULL; }