| /* Copyright 2017 The Chromium OS Authors. All rights reserved. |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include <poll.h> |
| #include <syslog.h> |
| |
| #include "audio_thread_log.h" |
| #include "cras_audio_area.h" |
| #include "cras_iodev.h" |
| #include "cras_non_empty_audio_handler.h" |
| #include "cras_rstream.h" |
| #include "cras_server_metrics.h" |
| #include "dev_stream.h" |
| #include "input_data.h" |
| #include "polled_interval_checker.h" |
| #include "utlist.h" |
| |
| #include "dev_io.h" |
| |
| static const struct timespec playback_wake_fuzz_ts = { |
| 0, 500 * 1000 /* 500 usec. */ |
| }; |
| |
| /* The maximum time to wait before checking the device's non-empty status. */ |
| static const int NON_EMPTY_UPDATE_INTERVAL_SEC = 5; |
| |
| /* |
| * The minimum number of consecutive seconds of empty audio that must be |
| * played before a device is considered to be playing empty audio. |
| */ |
| static const int MIN_EMPTY_PERIOD_SEC = 30; |
| |
| /* The number of devices playing/capturing non-empty stream(s). */ |
| static int non_empty_device_count = 0; |
| |
| /* Gets the master device which the stream is attached to. */ |
| static inline |
| struct cras_iodev *get_master_dev(const struct dev_stream *stream) |
| { |
| return (struct cras_iodev *)stream->stream->master_dev.dev_ptr; |
| } |
| |
| /* Updates the estimated sample rate of open device to all attached |
| * streams. |
| */ |
| static void update_estimated_rate(struct open_dev *adev) |
| { |
| struct cras_iodev *master_dev; |
| struct cras_iodev *dev = adev->dev; |
| struct dev_stream *dev_stream; |
| |
| DL_FOREACH(dev->streams, dev_stream) { |
| master_dev = get_master_dev(dev_stream); |
| if (master_dev == NULL) { |
| syslog(LOG_ERR, "Fail to find master open dev."); |
| continue; |
| } |
| |
| dev_stream_set_dev_rate(dev_stream, |
| dev->ext_format->frame_rate, |
| cras_iodev_get_est_rate_ratio(dev), |
| cras_iodev_get_est_rate_ratio(master_dev), |
| adev->coarse_rate_adjust); |
| } |
| } |
| |
| /* |
| * Counts the number of devices which are currently playing/capturing non-empty |
| * audio. |
| */ |
| static inline int count_non_empty_dev(struct open_dev *adevs) { |
| int count = 0; |
| struct open_dev *adev; |
| DL_FOREACH(adevs, adev) { |
| if (!adev->empty_pi || !pic_interval_elapsed(adev->empty_pi)) |
| count++; |
| } |
| return count; |
| } |
| |
| static void check_non_empty_state_transition(struct open_dev *adevs) { |
| int new_non_empty_dev_count = count_non_empty_dev(adevs); |
| |
| // If we have transitioned to or from a state with 0 non-empty devices, |
| // notify the main thread to update system state. |
| if ((non_empty_device_count == 0) != (new_non_empty_dev_count == 0)) |
| cras_non_empty_audio_send_msg( |
| new_non_empty_dev_count > 0 ? 1 : 0); |
| |
| non_empty_device_count = new_non_empty_dev_count; |
| } |
| |
| /* Asks any stream with room for more data. Sets the time stamp for all streams. |
| * Args: |
| * adev - The output device streams are attached to. |
| * Returns: |
| * 0 on success, negative error on failure. If failed, can assume that all |
| * streams have been removed from the device. |
| */ |
| static int fetch_streams(struct open_dev *adev) |
| { |
| struct dev_stream *dev_stream; |
| struct cras_iodev *odev = adev->dev; |
| int rc; |
| int delay; |
| |
| delay = cras_iodev_delay_frames(odev); |
| if (delay < 0) |
| return delay; |
| |
| DL_FOREACH(adev->dev->streams, dev_stream) { |
| struct cras_rstream *rstream = dev_stream->stream; |
| struct cras_audio_shm *shm = |
| cras_rstream_output_shm(rstream); |
| const struct timespec *next_cb_ts; |
| struct timespec now; |
| |
| clock_gettime(CLOCK_MONOTONIC_RAW, &now); |
| |
| if (dev_stream_is_pending_reply(dev_stream)) { |
| dev_stream_flush_old_audio_messages(dev_stream); |
| cras_rstream_record_fetch_interval(dev_stream->stream, |
| &now); |
| } |
| |
| if (cras_shm_get_frames(shm) < 0) |
| cras_rstream_set_is_draining(rstream, 1); |
| |
| if (cras_rstream_get_is_draining(dev_stream->stream)) |
| continue; |
| |
| next_cb_ts = dev_stream_next_cb_ts(dev_stream); |
| if (!next_cb_ts) |
| continue; |
| |
| /* Check if it's time to get more data from this stream. |
| * Allow for waking up a little early. */ |
| add_timespecs(&now, &playback_wake_fuzz_ts); |
| if (!timespec_after(&now, next_cb_ts)) |
| continue; |
| |
| if (!dev_stream_can_fetch(dev_stream)) { |
| ATLOG(atlog, AUDIO_THREAD_STREAM_SKIP_CB, |
| cras_rstream_id(rstream), |
| shm->area->write_offset[0], |
| shm->area->write_offset[1]); |
| continue; |
| } |
| |
| dev_stream_set_delay(dev_stream, delay); |
| |
| ATLOG(atlog, AUDIO_THREAD_FETCH_STREAM, rstream->stream_id, |
| cras_rstream_get_cb_threshold(rstream), delay); |
| |
| rc = dev_stream_request_playback_samples(dev_stream, &now); |
| if (rc < 0) { |
| syslog(LOG_ERR, "fetch err: %d for %x", |
| rc, cras_rstream_id(rstream)); |
| cras_rstream_set_is_draining(rstream, 1); |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* Gets the max delay frames of open input devices. */ |
| static int input_delay_frames(struct open_dev *adevs) |
| { |
| struct open_dev *adev; |
| int delay; |
| int max_delay = 0; |
| |
| DL_FOREACH(adevs, adev) { |
| if (!cras_iodev_is_open(adev->dev)) |
| continue; |
| delay = cras_iodev_delay_frames(adev->dev); |
| if (delay < 0) |
| return delay; |
| if (delay > max_delay) |
| max_delay = delay; |
| } |
| return max_delay; |
| } |
| |
| /* Sets the stream delay. |
| * Args: |
| * adev[in] - The device to capture from. |
| */ |
| static unsigned int set_stream_delay(struct open_dev *adev) |
| { |
| struct dev_stream *stream; |
| int delay; |
| |
| /* TODO(dgreid) - Setting delay from last dev only. */ |
| delay = input_delay_frames(adev); |
| |
| DL_FOREACH(adev->dev->streams, stream) { |
| if (stream->stream->flags & TRIGGER_ONLY) |
| continue; |
| |
| dev_stream_set_delay(stream, delay); |
| } |
| |
| return 0; |
| } |
| |
| /* Gets the minimum amount of space available for writing across all streams. |
| * Args: |
| * adev[in] - The device to capture from. |
| * write_limit[in] - Initial limit to number of frames to capture. |
| * limit_stream[out] - The pointer to the pointer of stream which |
| * causes capture limit. |
| * Output NULL if there is no stream that causes |
| * capture limit less than the initial limit. |
| */ |
| static unsigned int get_stream_limit( |
| struct open_dev *adev, |
| unsigned int write_limit, |
| struct dev_stream **limit_stream) |
| { |
| struct cras_rstream *rstream; |
| struct cras_audio_shm *shm; |
| struct dev_stream *stream; |
| unsigned int avail; |
| |
| *limit_stream = NULL; |
| |
| DL_FOREACH(adev->dev->streams, stream) { |
| rstream = stream->stream; |
| if (rstream->flags & TRIGGER_ONLY) |
| continue; |
| |
| shm = cras_rstream_input_shm(rstream); |
| if (cras_shm_check_write_overrun(shm)) |
| ATLOG(atlog, AUDIO_THREAD_READ_OVERRUN, |
| adev->dev->info.idx, rstream->stream_id, |
| shm->area->num_overruns); |
| avail = dev_stream_capture_avail(stream); |
| if (avail < write_limit) { |
| write_limit = avail; |
| *limit_stream = stream; |
| } |
| } |
| |
| return write_limit; |
| } |
| |
| /* |
| * The minimum wake time for a input device, which is 5ms. It's only used by |
| * function get_input_dev_max_wake_ts. |
| */ |
| static const struct timespec min_input_dev_wake_ts = { |
| 0, 5 * 1000 * 1000 /* 5 ms. */ |
| }; |
| |
| /* |
| * Get input device maximum sleep time, which is the approximate time that the |
| * device will have hw_level = buffer_size / 2 samples. Some devices have |
| * capture period = 2 so the audio_thread should wake up and consume some |
| * samples from hardware at that time. To prevent busy loop occurs, the returned |
| * sleep time should be >= 5ms. |
| * |
| * Returns: 0 on success negative error on device failure. |
| */ |
| static int get_input_dev_max_wake_ts( |
| struct open_dev *adev, |
| unsigned int curr_level, |
| struct timespec *res_ts) |
| { |
| struct timespec dev_wake_ts, now; |
| unsigned int dev_rate, half_buffer_size, target_frames; |
| |
| if(!adev || !adev->dev || !adev->dev->format || |
| !adev->dev->format->frame_rate || !adev->dev->buffer_size) |
| return -EINVAL; |
| |
| *res_ts = min_input_dev_wake_ts; |
| |
| dev_rate = adev->dev->format->frame_rate; |
| half_buffer_size = adev->dev->buffer_size / 2; |
| if(curr_level < half_buffer_size) |
| target_frames = half_buffer_size - curr_level; |
| else |
| target_frames = 0; |
| |
| cras_frames_to_time(target_frames, dev_rate, &dev_wake_ts); |
| |
| if (timespec_after(&dev_wake_ts, res_ts)) { |
| *res_ts = dev_wake_ts; |
| } |
| |
| clock_gettime(CLOCK_MONOTONIC_RAW, &now); |
| add_timespecs(res_ts, &now); |
| return 0; |
| } |
| |
| /* |
| * Set wake_ts for this device to be the earliest wake up time for |
| * dev_streams. |
| */ |
| static int set_input_dev_wake_ts(struct open_dev *adev) |
| { |
| int rc; |
| struct timespec level_tstamp, wake_time_out, min_ts, now, dev_wake_ts; |
| unsigned int curr_level, cap_limit; |
| struct dev_stream *stream; |
| struct dev_stream *cap_limit_stream; |
| |
| /* Limit the sleep time to 20 seconds. */ |
| min_ts.tv_sec = 20; |
| min_ts.tv_nsec = 0; |
| clock_gettime(CLOCK_MONOTONIC_RAW, &now); |
| add_timespecs(&min_ts, &now); |
| |
| rc = cras_iodev_frames_queued(adev->dev, &level_tstamp); |
| if (rc < 0) |
| return rc; |
| curr_level = rc; |
| if (!timespec_is_nonzero(&level_tstamp)) |
| clock_gettime(CLOCK_MONOTONIC_RAW, &level_tstamp); |
| |
| |
| cap_limit = get_stream_limit(adev, UINT_MAX, &cap_limit_stream); |
| |
| /* |
| * Loop through streams to find the earliest time audio thread |
| * should wake up. |
| */ |
| DL_FOREACH(adev->dev->streams, stream) { |
| wake_time_out = min_ts; |
| rc = dev_stream_wake_time( |
| stream, |
| curr_level, |
| &level_tstamp, |
| cap_limit, |
| cap_limit_stream == stream, |
| &wake_time_out); |
| |
| /* |
| * rc > 0 means there is no need to set wake up time for this |
| * stream. |
| */ |
| if (rc > 0) |
| continue; |
| |
| if (rc < 0) |
| return rc; |
| |
| if (timespec_after(&min_ts, &wake_time_out)) { |
| min_ts = wake_time_out; |
| } |
| } |
| |
| if(adev->dev->active_node && |
| adev->dev->active_node->type != CRAS_NODE_TYPE_HOTWORD) { |
| rc = get_input_dev_max_wake_ts(adev, curr_level, &dev_wake_ts); |
| if(rc < 0) { |
| syslog(LOG_ERR, |
| "Failed to call get_input_dev_max_wake_ts." |
| "rc = %d", rc); |
| } else if(timespec_after(&min_ts, &dev_wake_ts)) { |
| min_ts = dev_wake_ts; |
| } |
| } |
| |
| adev->wake_ts = min_ts; |
| return rc; |
| } |
| |
| /* Read samples from an input device to the specified stream. |
| * Args: |
| * adev - The device to capture samples from. |
| * Returns 0 on success. |
| */ |
| static int capture_to_streams(struct open_dev *adev) |
| { |
| struct cras_iodev *idev = adev->dev; |
| snd_pcm_uframes_t remainder, hw_level, cap_limit; |
| struct timespec hw_tstamp; |
| int rc; |
| struct dev_stream *cap_limit_stream; |
| struct dev_stream *stream; |
| |
| DL_FOREACH(adev->dev->streams, stream) |
| dev_stream_flush_old_audio_messages(stream); |
| |
| rc = cras_iodev_frames_queued(idev, &hw_tstamp); |
| if (rc < 0) |
| return rc; |
| hw_level = rc; |
| |
| cras_iodev_update_highest_hw_level(idev, hw_level); |
| |
| ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_TSTAMP, idev->info.idx, |
| hw_tstamp.tv_sec, hw_tstamp.tv_nsec); |
| if (timespec_is_nonzero(&hw_tstamp)) { |
| if (hw_level < idev->min_cb_level / 2) |
| adev->coarse_rate_adjust = 1; |
| else if (hw_level > idev->max_cb_level * 2) |
| adev->coarse_rate_adjust = -1; |
| else |
| adev->coarse_rate_adjust = 0; |
| if (cras_iodev_update_rate(idev, hw_level, &hw_tstamp)) |
| update_estimated_rate(adev); |
| } |
| |
| cap_limit = get_stream_limit(adev, hw_level, &cap_limit_stream); |
| set_stream_delay(adev); |
| |
| remainder = MIN(hw_level, cap_limit); |
| |
| ATLOG(atlog, AUDIO_THREAD_READ_AUDIO, idev->info.idx, |
| hw_level, remainder); |
| |
| if (cras_iodev_state(idev) != CRAS_IODEV_STATE_NORMAL_RUN) |
| return 0; |
| |
| while (remainder > 0) { |
| struct cras_audio_area *area = NULL; |
| unsigned int nread, total_read; |
| |
| nread = remainder; |
| |
| rc = cras_iodev_get_input_buffer(idev, &nread); |
| if (rc < 0 || nread == 0) |
| return rc; |
| |
| DL_FOREACH(adev->dev->streams, stream) { |
| unsigned int this_read; |
| unsigned int area_offset; |
| float software_gain_scaler; |
| |
| if ((stream->stream->flags & TRIGGER_ONLY) && |
| stream->stream->triggered) |
| continue; |
| |
| input_data_get_for_stream(idev->input_data, stream->stream, |
| idev->buf_state, |
| &area, &area_offset); |
| /* |
| * APM has more advanced gain control mechanism, so |
| * don't apply the CRAS software gain to this stream |
| * if APM is used. |
| */ |
| software_gain_scaler = stream->stream->apm_list |
| ? 1.0f |
| : cras_iodev_get_software_gain_scaler(idev); |
| |
| this_read = dev_stream_capture( |
| stream, area, area_offset, |
| software_gain_scaler); |
| |
| input_data_put_for_stream(idev->input_data, stream->stream, |
| idev->buf_state, this_read); |
| } |
| |
| rc = cras_iodev_put_input_buffer(idev); |
| if (rc < 0) |
| return rc; |
| |
| total_read = rc; |
| remainder -= nread; |
| |
| if (total_read < nread) |
| break; |
| } |
| |
| ATLOG(atlog, AUDIO_THREAD_READ_AUDIO_DONE, remainder, 0, 0); |
| |
| return 0; |
| } |
| |
| /* Fill the buffer with samples from the attached streams. |
| * Args: |
| * odevs - The list of open output devices, provided so streams can be |
| * removed from all devices on error. |
| * adev - The device to write to. |
| * dst - The buffer to put the samples in (returned from snd_pcm_mmap_begin) |
| * write_limit - The maximum number of frames to write to dst. |
| * |
| * Returns: |
| * The number of frames rendered on success, a negative error code otherwise. |
| * This number of frames is the minimum of the amount of frames each stream |
| * could provide which is the maximum that can currently be rendered. |
| */ |
| static int write_streams(struct open_dev **odevs, |
| struct open_dev *adev, |
| uint8_t *dst, |
| size_t write_limit) |
| { |
| struct cras_iodev *odev = adev->dev; |
| struct dev_stream *curr; |
| unsigned int max_offset = 0; |
| unsigned int frame_bytes = cras_get_format_bytes(odev->ext_format); |
| unsigned int num_playing = 0; |
| unsigned int drain_limit = write_limit; |
| |
| /* Mix as much as we can, the minimum fill level of any stream. */ |
| max_offset = cras_iodev_max_stream_offset(odev); |
| |
| /* Mix as much as we can, the minimum fill level of any stream. */ |
| DL_FOREACH(adev->dev->streams, curr) { |
| int dev_frames; |
| |
| /* If this is a single output dev stream, updates the latest |
| * number of frames for playback. */ |
| if (dev_stream_attached_devs(curr) == 1) |
| dev_stream_update_frames(curr); |
| |
| dev_frames = dev_stream_playback_frames(curr); |
| if (dev_frames < 0) { |
| dev_io_remove_stream( |
| odevs, |
| curr->stream, NULL); |
| continue; |
| } |
| ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_STREAM, |
| curr->stream->stream_id, dev_frames, |
| dev_stream_is_pending_reply(curr)); |
| if (cras_rstream_get_is_draining(curr->stream)) { |
| drain_limit = MIN((size_t)dev_frames, drain_limit); |
| if (!dev_frames) |
| dev_io_remove_stream( |
| odevs, |
| curr->stream, NULL); |
| } else { |
| write_limit = MIN((size_t)dev_frames, write_limit); |
| num_playing++; |
| } |
| } |
| |
| if (!num_playing) |
| write_limit = drain_limit; |
| |
| if (write_limit > max_offset) |
| memset(dst + max_offset * frame_bytes, 0, |
| (write_limit - max_offset) * frame_bytes); |
| |
| ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIX, |
| write_limit, max_offset, 0); |
| |
| DL_FOREACH(adev->dev->streams, curr) { |
| unsigned int offset; |
| int nwritten; |
| |
| offset = cras_iodev_stream_offset(odev, curr); |
| if (offset >= write_limit) |
| continue; |
| nwritten = dev_stream_mix(curr, odev->ext_format, |
| dst + frame_bytes * offset, |
| write_limit - offset); |
| |
| if (nwritten < 0) { |
| dev_io_remove_stream(odevs, curr->stream, NULL); |
| continue; |
| } |
| |
| cras_iodev_stream_written(odev, curr, nwritten); |
| } |
| |
| write_limit = cras_iodev_all_streams_written(odev); |
| |
| ATLOG(atlog, AUDIO_THREAD_WRITE_STREAMS_MIXED, write_limit, 0, 0); |
| |
| return write_limit; |
| } |
| |
| /* Update next wake up time of the device. |
| * Args: |
| * adev[in] - The device to update to. |
| * hw_level[out] - Pointer to number of frames in hardware. |
| */ |
| void update_dev_wakeup_time(struct open_dev *adev, unsigned int *hw_level) |
| { |
| struct timespec now; |
| struct timespec sleep_time; |
| double est_rate; |
| unsigned int frames_to_play_in_sleep; |
| |
| clock_gettime(CLOCK_MONOTONIC_RAW, &now); |
| |
| frames_to_play_in_sleep = cras_iodev_frames_to_play_in_sleep( |
| adev->dev, hw_level, &adev->wake_ts); |
| if (!timespec_is_nonzero(&adev->wake_ts)) |
| adev->wake_ts = now; |
| |
| if (cras_iodev_state(adev->dev) == CRAS_IODEV_STATE_NORMAL_RUN) |
| cras_iodev_update_highest_hw_level(adev->dev, *hw_level); |
| |
| est_rate = adev->dev->ext_format->frame_rate * |
| cras_iodev_get_est_rate_ratio(adev->dev); |
| |
| ATLOG(atlog, AUDIO_THREAD_SET_DEV_WAKE, adev->dev->info.idx, |
| *hw_level, frames_to_play_in_sleep); |
| |
| cras_frames_to_time_precise( |
| frames_to_play_in_sleep, |
| est_rate, |
| &sleep_time); |
| |
| add_timespecs(&adev->wake_ts, &sleep_time); |
| |
| ATLOG(atlog, AUDIO_THREAD_DEV_SLEEP_TIME, adev->dev->info.idx, |
| adev->wake_ts.tv_sec, adev->wake_ts.tv_nsec); |
| } |
| |
| /* Returns 0 on success negative error on device failure. */ |
| int write_output_samples(struct open_dev **odevs, |
| struct open_dev *adev, |
| struct cras_fmt_conv *output_converter) |
| { |
| struct cras_iodev *odev = adev->dev; |
| unsigned int hw_level; |
| struct timespec hw_tstamp; |
| unsigned int frames, fr_to_req; |
| snd_pcm_sframes_t written; |
| snd_pcm_uframes_t total_written = 0; |
| int rc; |
| int non_empty = 0; |
| int *non_empty_ptr = NULL; |
| uint8_t *dst = NULL; |
| struct cras_audio_area *area = NULL; |
| |
| /* Possibly fill zeros for no_stream state and possibly transit state. |
| */ |
| rc = cras_iodev_prepare_output_before_write_samples(odev); |
| if (rc < 0) { |
| syslog(LOG_ERR, "Failed to prepare output dev for write"); |
| return rc; |
| } |
| |
| if (cras_iodev_state(odev) != CRAS_IODEV_STATE_NORMAL_RUN) |
| return 0; |
| |
| rc = cras_iodev_frames_queued(odev, &hw_tstamp); |
| if (rc < 0) |
| return rc; |
| hw_level = rc; |
| |
| ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_TSTAMP, adev->dev->info.idx, |
| hw_tstamp.tv_sec, hw_tstamp.tv_nsec); |
| if (timespec_is_nonzero(&hw_tstamp)) { |
| if (hw_level < odev->min_cb_level / 2) |
| adev->coarse_rate_adjust = 1; |
| else if (hw_level > odev->max_cb_level * 2) |
| adev->coarse_rate_adjust = -1; |
| else |
| adev->coarse_rate_adjust = 0; |
| |
| if (cras_iodev_update_rate(odev, hw_level, &hw_tstamp)) |
| update_estimated_rate(adev); |
| } |
| ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO, adev->dev->info.idx, hw_level, 0); |
| |
| /* Don't request more than hardware can hold. Note that min_buffer_level |
| * has been subtracted from the actual hw_level so we need to take it |
| * into account here. */ |
| fr_to_req = cras_iodev_buffer_avail(odev, hw_level); |
| |
| /* Have to loop writing to the device, will be at most 2 loops, this |
| * only happens when the circular buffer is at the end and returns us a |
| * partial area to write to from mmap_begin */ |
| while (total_written < fr_to_req) { |
| frames = fr_to_req - total_written; |
| rc = cras_iodev_get_output_buffer(odev, &area, &frames); |
| if (rc < 0) |
| return rc; |
| |
| /* TODO(dgreid) - This assumes interleaved audio. */ |
| dst = area->channels[0].buf; |
| written = write_streams(odevs, adev, dst, frames); |
| if (written < 0) /* pcm has been closed */ |
| return (int)written; |
| |
| if (written < (snd_pcm_sframes_t)frames) |
| /* Got all the samples from client that we can, but it |
| * won't fill the request. */ |
| fr_to_req = 0; /* break out after committing samples */ |
| |
| // This interval is lazily initialized once per device. |
| // Note that newly opened devices are considered non-empty |
| // (until their status is updated through the normal flow). |
| if (!adev->non_empty_check_pi) { |
| adev->non_empty_check_pi = pic_polled_interval_create( |
| NON_EMPTY_UPDATE_INTERVAL_SEC); |
| } |
| |
| // If we were empty last iteration, or the sampling interval |
| // has elapsed, check for emptiness. |
| if (adev->empty_pi || |
| pic_interval_elapsed(adev->non_empty_check_pi)) { |
| non_empty_ptr = &non_empty; |
| pic_interval_reset(adev->non_empty_check_pi); |
| } |
| |
| rc = cras_iodev_put_output_buffer(odev, dst, written, |
| non_empty_ptr, |
| output_converter); |
| |
| if (rc < 0) |
| return rc; |
| total_written += written; |
| |
| if (non_empty && adev->empty_pi) { |
| // We're not empty, but we were previously. |
| // Reset the empty period. |
| pic_polled_interval_destroy(&adev->empty_pi); |
| } |
| |
| if (non_empty_ptr && !non_empty && !adev->empty_pi) |
| // We checked for emptiness, we were empty, and we |
| // previously weren't. Start the empty period. |
| adev->empty_pi = pic_polled_interval_create( |
| MIN_EMPTY_PERIOD_SEC); |
| } |
| |
| ATLOG(atlog, AUDIO_THREAD_FILL_AUDIO_DONE, hw_level, |
| total_written, odev->min_cb_level); |
| |
| return total_written; |
| } |
| |
| /* |
| * Public funcitons. |
| */ |
| |
| int dev_io_send_captured_samples(struct open_dev *idev_list) |
| { |
| struct open_dev *adev; |
| int rc; |
| |
| // TODO(dgreid) - once per rstream, not once per dev_stream. |
| DL_FOREACH(idev_list, adev) { |
| struct dev_stream *stream; |
| |
| if (!cras_iodev_is_open(adev->dev)) |
| continue; |
| |
| /* Post samples to rstream if there are enough samples. */ |
| DL_FOREACH(adev->dev->streams, stream) { |
| dev_stream_capture_update_rstream(stream); |
| } |
| |
| /* Set wake_ts for this device. */ |
| rc = set_input_dev_wake_ts(adev); |
| if (rc < 0) |
| return rc; |
| } |
| |
| return 0; |
| } |
| |
| static void handle_dev_err( |
| int err_rc, |
| struct open_dev **odevs, |
| struct open_dev *adev) |
| { |
| if (err_rc == -EPIPE) { |
| /* Handle severe underrun. */ |
| ATLOG(atlog, AUDIO_THREAD_SEVERE_UNDERRUN, |
| adev->dev->info.idx, 0, 0); |
| cras_iodev_reset_request(adev->dev); |
| } else { |
| /* Device error, close it. */ |
| dev_io_rm_open_dev(odevs, adev); |
| } |
| } |
| |
| int dev_io_capture(struct open_dev **list) |
| { |
| struct open_dev *idev_list = *list; |
| struct open_dev *adev; |
| int rc; |
| |
| DL_FOREACH(idev_list, adev) { |
| if (!cras_iodev_is_open(adev->dev)) |
| continue; |
| rc = capture_to_streams(adev); |
| if (rc < 0) |
| handle_dev_err(rc, list, adev); |
| } |
| |
| return 0; |
| } |
| |
| void dev_io_playback_fetch(struct open_dev *odev_list) |
| { |
| struct open_dev *adev; |
| |
| DL_FOREACH(odev_list, adev) { |
| if (!cras_iodev_is_open(adev->dev)) |
| continue; |
| fetch_streams(adev); |
| } |
| } |
| |
| int dev_io_playback_write(struct open_dev **odevs, |
| struct cras_fmt_conv *output_converter) |
| { |
| struct open_dev *adev; |
| struct dev_stream *curr; |
| int rc; |
| unsigned int hw_level, total_written; |
| |
| /* For multiple output case, update the number of queued frames in shm |
| * of all streams before starting write output samples. */ |
| adev = *odevs; |
| if (adev && adev->next) { |
| DL_FOREACH(*odevs, adev) { |
| DL_FOREACH(adev->dev->streams, curr) |
| dev_stream_update_frames(curr); |
| } |
| } |
| |
| DL_FOREACH(*odevs, adev) { |
| if (!cras_iodev_is_open(adev->dev)) |
| continue; |
| |
| rc = write_output_samples(odevs, adev, output_converter); |
| if (rc < 0) { |
| handle_dev_err(rc, odevs, adev); |
| } else { |
| total_written = rc; |
| |
| /* |
| * Skip the underrun check and device wake up time update if |
| * device should not wake up. |
| */ |
| if (!cras_iodev_odev_should_wake(adev->dev)) |
| continue; |
| |
| /* |
| * Update device wake up time and get the new hardware |
| * level. |
| */ |
| update_dev_wakeup_time(adev, &hw_level); |
| |
| /* |
| * If new hardware level is less than or equal to the |
| * written frames, we can suppose underrun happened. But |
| * keep in mind there may have a false positive. If |
| * hardware level changed just after frames being |
| * written, we may get hw_level <= total_written here |
| * without underrun happened. However, we can still |
| * treat it as underrun because it is an abnormal state |
| * we should handle it. |
| */ |
| if (hw_level <= total_written) { |
| ATLOG(atlog, AUDIO_THREAD_UNDERRUN, |
| adev->dev->info.idx, |
| hw_level, total_written); |
| rc = cras_iodev_output_underrun(adev->dev); |
| if(rc < 0) { |
| handle_dev_err(rc, odevs, adev); |
| } else { |
| update_dev_wakeup_time(adev, &hw_level); |
| } |
| } |
| } |
| } |
| |
| /* TODO(dgreid) - once per rstream, not once per dev_stream. */ |
| DL_FOREACH(*odevs, adev) { |
| struct dev_stream *stream; |
| if (!cras_iodev_is_open(adev->dev)) |
| continue; |
| DL_FOREACH(adev->dev->streams, stream) { |
| dev_stream_playback_update_rstream(stream); |
| } |
| } |
| |
| return 0; |
| } |
| |
| void dev_io_run(struct open_dev **odevs, struct open_dev **idevs, |
| struct cras_fmt_conv *output_converter) |
| { |
| pic_update_current_time(); |
| |
| dev_io_playback_fetch(*odevs); |
| dev_io_capture(idevs); |
| dev_io_send_captured_samples(*idevs); |
| dev_io_playback_write(odevs, output_converter); |
| |
| check_non_empty_state_transition(*odevs); |
| } |
| |
| static int input_adev_ignore_wake(const struct open_dev *adev) |
| { |
| if (!cras_iodev_is_open(adev->dev)) |
| return 1; |
| |
| if (!adev->dev->active_node) |
| return 1; |
| |
| if (adev->dev->active_node->type == CRAS_NODE_TYPE_HOTWORD && |
| !cras_iodev_input_streaming(adev->dev)) |
| return 1; |
| |
| return 0; |
| } |
| |
| int dev_io_next_input_wake(struct open_dev **idevs, struct timespec *min_ts) |
| { |
| struct open_dev *adev; |
| int ret = 0; /* The total number of devices to wait on. */ |
| |
| DL_FOREACH(*idevs, adev) { |
| if (input_adev_ignore_wake(adev)) |
| continue; |
| ret++; |
| ATLOG(atlog, AUDIO_THREAD_DEV_SLEEP_TIME, adev->dev->info.idx, |
| adev->wake_ts.tv_sec, adev->wake_ts.tv_nsec); |
| if (timespec_after(min_ts, &adev->wake_ts)) |
| *min_ts = adev->wake_ts; |
| } |
| |
| return ret; |
| } |
| |
| struct open_dev *dev_io_find_open_dev(struct open_dev *odev_list, |
| const struct cras_iodev *dev) |
| { |
| struct open_dev *odev; |
| DL_FOREACH(odev_list, odev) |
| if (odev->dev == dev) |
| return odev; |
| return NULL; |
| } |
| |
| void dev_io_rm_open_dev(struct open_dev **odev_list, |
| struct open_dev *dev_to_rm) |
| { |
| struct open_dev *odev; |
| struct dev_stream *dev_stream; |
| |
| /* Do nothing if dev_to_rm wasn't already in the active dev list. */ |
| odev = dev_io_find_open_dev(*odev_list, dev_to_rm->dev); |
| if (!odev) |
| return; |
| |
| |
| DL_DELETE(*odev_list, dev_to_rm); |
| |
| /* Metrics logs the number of underruns of this device. */ |
| cras_server_metrics_num_underruns( |
| cras_iodev_get_num_underruns(dev_to_rm->dev)); |
| |
| /* Metrics logs the highest_hw_level of this device. */ |
| cras_server_metrics_highest_hw_level( |
| dev_to_rm->dev->highest_hw_level, dev_to_rm->dev->direction); |
| |
| check_non_empty_state_transition(*odev_list); |
| |
| ATLOG(atlog, AUDIO_THREAD_DEV_REMOVED, dev_to_rm->dev->info.idx, 0, 0); |
| |
| DL_FOREACH(dev_to_rm->dev->streams, dev_stream) { |
| cras_iodev_rm_stream(dev_to_rm->dev, dev_stream->stream); |
| dev_stream_destroy(dev_stream); |
| } |
| |
| if (dev_to_rm->empty_pi) |
| pic_polled_interval_destroy(&dev_to_rm->empty_pi); |
| if (dev_to_rm->non_empty_check_pi) |
| pic_polled_interval_destroy(&dev_to_rm->non_empty_check_pi); |
| free(dev_to_rm); |
| } |
| |
| static void delete_stream_from_dev(struct cras_iodev *dev, |
| struct cras_rstream *stream) |
| { |
| struct dev_stream *out; |
| |
| out = cras_iodev_rm_stream(dev, stream); |
| if (out) |
| dev_stream_destroy(out); |
| } |
| |
| int dev_io_remove_stream(struct open_dev **dev_list, |
| struct cras_rstream *stream, |
| struct cras_iodev *dev) |
| { |
| struct open_dev *open_dev; |
| struct timespec delay; |
| unsigned fetch_delay_msec; |
| |
| /* Metrics log the longest fetch delay of this stream. */ |
| if (timespec_after(&stream->longest_fetch_interval, |
| &stream->sleep_interval_ts)) { |
| subtract_timespecs(&stream->longest_fetch_interval, |
| &stream->sleep_interval_ts, |
| &delay); |
| fetch_delay_msec = delay.tv_sec * 1000 + |
| delay.tv_nsec / 1000000; |
| if (fetch_delay_msec) |
| cras_server_metrics_longest_fetch_delay( |
| fetch_delay_msec); |
| } |
| |
| ATLOG(atlog, AUDIO_THREAD_STREAM_REMOVED, stream->stream_id, 0, 0); |
| |
| if (dev == NULL) { |
| DL_FOREACH(*dev_list, open_dev) { |
| delete_stream_from_dev(open_dev->dev, stream); |
| } |
| } else { |
| delete_stream_from_dev(dev, stream); |
| } |
| |
| return 0; |
| } |