struct a2dp_stream_out {
struct audio_stream_out stream;
struct a2dp_stream_common common;
+ uint64_t frames_presented; // frames written, never reset
+ uint64_t frames_rendered; // frames written, reset on standby
};
struct a2dp_stream_in {
pthread_mutex_unlock(&out->common.lock);
sent = skt_write(out->common.audio_fd, buffer, bytes);
- if (sent == -1)
- {
+ if (sent == -1) {
skt_disconnect(out->common.audio_fd);
out->common.audio_fd = AUDIO_SKT_DISCONNECTED;
if (out->common.state != AUDIO_A2DP_STATE_SUSPENDED)
out->common.state = AUDIO_A2DP_STATE_STOPPED;
else
ERROR("write failed : stream suspended, avoid resetting state");
+ } else {
+ const size_t frames = bytes / audio_stream_out_frame_size(stream);
+ out->frames_rendered += frames;
+ out->frames_presented += frames;
}
DEBUG("wrote %d bytes out of %zu bytes", sent, bytes);
// Do nothing in SUSPENDED state.
if (out->common.state != AUDIO_A2DP_STATE_SUSPENDED)
retVal = suspend_audio_datapath(&out->common, true);
+ out->frames_rendered = 0; // rendered is reset, presented is not
pthread_mutex_unlock (&out->common.lock);
return retVal;
return -ENOSYS;
}
+static int out_get_presentation_position(const struct audio_stream_out *stream,
+ uint64_t *frames, struct timespec *timestamp)
+{
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
+
+ FNLOG();
+ if (stream == NULL || frames == NULL || timestamp == NULL)
+ return -EINVAL;
+ int ret = -EWOULDBLOCK;
+ pthread_mutex_lock(&out->common.lock);
+ uint64_t latency_frames = (uint64_t)out_get_latency(stream) * out->common.cfg.rate / 1000;
+ if (out->frames_presented >= latency_frames) {
+ *frames = out->frames_presented - latency_frames;
+ clock_gettime(CLOCK_MONOTONIC, timestamp); // could also be associated with out_write().
+ ret = 0;
+ }
+ pthread_mutex_unlock(&out->common.lock);
+ return ret;
+}
static int out_get_render_position(const struct audio_stream_out *stream,
uint32_t *dsp_frames)
{
- UNUSED(stream);
- UNUSED(dsp_frames);
+ struct a2dp_stream_out *out = (struct a2dp_stream_out *)stream;
FNLOG();
- return -EINVAL;
+ if (stream == NULL || dsp_frames == NULL)
+ return -EINVAL;
+
+ pthread_mutex_lock(&out->common.lock);
+ uint64_t latency_frames = (uint64_t)out_get_latency(stream) * out->common.cfg.rate / 1000;
+ if (out->frames_rendered >= latency_frames) {
+ *dsp_frames = (uint32_t)(out->frames_rendered - latency_frames);
+ } else {
+ *dsp_frames = 0;
+ }
+ pthread_mutex_unlock(&out->common.lock);
+ return 0;
}
static int out_add_audio_effect(const struct audio_stream *stream, effect_handle_t effect)
out->stream.set_volume = out_set_volume;
out->stream.write = out_write;
out->stream.get_render_position = out_get_render_position;
+ out->stream.get_presentation_position = out_get_presentation_position;
+
/* initialize a2dp specifics */
a2dp_stream_common_init(&out->common);