普通文本  |  460行  |  15.59 KB

// 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 <memory>
#include <stdint.h>
#include <stdio.h>
#include <time.h>

#include <gtest/gtest.h>

extern "C" {
#include "dev_io.h" // tested
#include "dev_stream.h" // tested
#include "cras_rstream.h" // stubbed
#include "cras_iodev.h" // stubbed
#include "cras_shm.h"
#include "cras_types.h"
#include "utlist.h"

struct audio_thread_event_log* atlog;
}

#include "dev_io_stubs.h"
#include "iodev_stub.h"
#include "rstream_stub.h"

#define FAKE_POLL_FD 33

namespace {

class TimingSuite : public testing::Test{
 protected:
  virtual void SetUp() {
    atlog = static_cast<audio_thread_event_log*>(calloc(1, sizeof(*atlog)));
    iodev_stub_reset();
    rstream_stub_reset();
  }

  virtual void TearDown() {
    free(atlog);
  }

  timespec SingleInputDevNextWake(
      size_t dev_cb_threshold,
      size_t dev_level,
      const timespec* level_timestamp,
      cras_audio_format* dev_format,
      const std::vector<StreamPtr>& streams,
      CRAS_NODE_TYPE active_node_type = CRAS_NODE_TYPE_MIC) {
    struct open_dev* dev_list_ = NULL;

    DevicePtr dev = create_device(CRAS_STREAM_INPUT, dev_cb_threshold,
                                  dev_format, active_node_type);
    dev->dev->input_streaming = true;
    DL_APPEND(dev_list_, dev->odev.get());

    for (auto const& stream : streams) {
      add_stream_to_dev(dev->dev, stream);
    }

    // Set response for frames_queued.
    iodev_stub_frames_queued(dev->dev.get(), dev_level, *level_timestamp);

    dev_io_send_captured_samples(dev_list_);

    struct timespec dev_time;
    dev_time.tv_sec = level_timestamp->tv_sec + 500; // Far in the future.
    dev_io_next_input_wake(&dev_list_, &dev_time);
    return dev_time;
  }
};

// One device, one stream, write a callback of data and check the sleep time is
// one more wakeup interval.
TEST_F(TimingSuite, WaitAfterFill) {
  const size_t cb_threshold = 480;

  cras_audio_format format;
  fill_audio_format(&format, 48000);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
  // rstream's next callback is now and there is enough data to fill.
  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
  stream->rstream->next_cb_ts = start;
  AddFakeDataToStream(stream.get(), 480);

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  timespec dev_time = SingleInputDevNextWake(cb_threshold, 0, &start,
                                             &format, streams);

  // The next callback should be scheduled 10ms in the future.
  // And the next wake up should reflect the only attached stream.
  EXPECT_EQ(dev_time.tv_sec, streams[0]->rstream->next_cb_ts.tv_sec);
  EXPECT_EQ(dev_time.tv_nsec, streams[0]->rstream->next_cb_ts.tv_nsec);
}

// One device with one stream which has block_size larger than the device buffer
// level. If the device buffer level = 0, the input device wake time should be
// set to (buffer_size / 2) / device_rate secs.
TEST_F(TimingSuite, LargeCallbackStreamWithEmptyBuffer) {
  const size_t cb_threshold = 3000;
  const size_t dev_cb_threshold = 1200;
  const size_t dev_level = 0;

  cras_audio_format format;
  fill_audio_format(&format, 48000);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
  stream->rstream->next_cb_ts = start;

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  timespec dev_time = SingleInputDevNextWake(
      dev_cb_threshold, dev_level, &start, &format, streams);

  struct timespec delta;
  subtract_timespecs(&dev_time, &start, &delta);
  // The next dev wake ts should be 25ms since the buffer level is empty and
  // 1200 / 48000 = 0.025.
  EXPECT_EQ(delta.tv_sec, 0);
  EXPECT_LT(delta.tv_nsec, 25000000 + 5000 * 1000);
  EXPECT_GT(delta.tv_nsec, 25000000 - 5000 * 1000);
}

// One device with one stream which has block_size larger than the device buffer
// level. If the device buffer level = buffer_size / 2, the input device wake
// time should be set to max(0, 5ms) = 5ms to prevent busy loop occurs.
TEST_F(TimingSuite, LargeCallbackStreamWithHalfFullBuffer) {
  const size_t cb_threshold = 3000;
  const size_t dev_cb_threshold = 1200;
  const size_t dev_level = 1200;

  cras_audio_format format;
  fill_audio_format(&format, 48000);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
  stream->rstream->next_cb_ts = start;

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  timespec dev_time = SingleInputDevNextWake(
      dev_cb_threshold, dev_level, &start, &format, streams);

  struct timespec delta;
  subtract_timespecs(&dev_time, &start, &delta);
  // The next dev wake ts should be 5ms since the buffer level is half full.
  EXPECT_EQ(delta.tv_sec, 0);
  EXPECT_LT(delta.tv_nsec, 5000000 + 5000 * 1000);
  EXPECT_GT(delta.tv_nsec, 5000000 - 5000 * 1000);
}

// One device(48k), one stream(44.1k), write a callback of data and check that
// the sleep time is correct when doing SRC.
TEST_F(TimingSuite, WaitAfterFillSRC) {
  cras_audio_format dev_format;
  fill_audio_format(&dev_format, 48000);
  cras_audio_format stream_format;
  fill_audio_format(&stream_format, 44100);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, 441, &stream_format);
  // rstream's next callback is now and there is enough data to fill.
  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
  stream->rstream->next_cb_ts = start;
  AddFakeDataToStream(stream.get(), 441);

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  timespec dev_time = SingleInputDevNextWake(480, 0, &start,
                                             &dev_format, streams);

  // The next callback should be scheduled 10ms in the future.
  struct timespec delta;
  subtract_timespecs(&dev_time, &start, &delta);
  EXPECT_LT(9900 * 1000, delta.tv_nsec);
  EXPECT_GT(10100 * 1000, delta.tv_nsec);
}

// One device, two streams. One stream is ready the other still needs data.
// Checks that the sleep interval is based on the time the device will take to
// supply the needed samples for stream2.
TEST_F(TimingSuite, WaitTwoStreamsSameFormat) {
  const size_t cb_threshold = 480;

  cras_audio_format format;
  fill_audio_format(&format, 48000);

  // stream1's next callback is now and there is enough data to fill.
  StreamPtr stream1 =
      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
  stream1->rstream->next_cb_ts = start;
  AddFakeDataToStream(stream1.get(), cb_threshold);

  // stream2 is only half full.
  StreamPtr stream2  =
      create_stream(1, 1, CRAS_STREAM_INPUT, cb_threshold, &format);
  stream2->rstream->next_cb_ts = start;
  AddFakeDataToStream(stream2.get(), 240);

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream1));
  streams.emplace_back(std::move(stream2));
  timespec dev_time = SingleInputDevNextWake(cb_threshold, 0, &start,
                                             &format, streams);

  // Should wait for approximately 5 milliseconds for 240 samples at 48k.
  struct timespec delta2;
  subtract_timespecs(&dev_time, &start, &delta2);
  EXPECT_LT(4900 * 1000, delta2.tv_nsec);
  EXPECT_GT(5100 * 1000, delta2.tv_nsec);
}

// One device(44.1), two streams(44.1, 48). One stream is ready the other still
// needs data. Checks that the sleep interval is based on the time the device
// will take to supply the needed samples for stream2, stream2 is sample rate
// converted from the 44.1k device to the 48k stream.
TEST_F(TimingSuite, WaitTwoStreamsDifferentRates) {
  cras_audio_format s1_format, s2_format;
  fill_audio_format(&s1_format, 44100);
  fill_audio_format(&s2_format, 48000);

  // stream1's next callback is now and there is enough data to fill.
  StreamPtr stream1 =
      create_stream(1, 1, CRAS_STREAM_INPUT, 441, &s1_format);
  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);
  stream1->rstream->next_cb_ts = start;
  AddFakeDataToStream(stream1.get(), 441);
  // stream2's next callback is now but there is only half a callback of data.
  StreamPtr stream2  =
      create_stream(1, 1, CRAS_STREAM_INPUT, 480, &s2_format);
  stream2->rstream->next_cb_ts = start;
  AddFakeDataToStream(stream2.get(), 240);

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream1));
  streams.emplace_back(std::move(stream2));
  timespec dev_time = SingleInputDevNextWake(441, 0, &start,
                                             &s1_format, streams);

  // Should wait for approximately 5 milliseconds for 240 48k samples from the
  // 44.1k device.
  struct timespec delta2;
  subtract_timespecs(&dev_time, &start, &delta2);
  EXPECT_LT(4900 * 1000, delta2.tv_nsec);
  EXPECT_GT(5100 * 1000, delta2.tv_nsec);
}

// One device, two streams. Both streams get a full callback of data and the
// device has enough samples for the next callback already. Checks that the
// shorter of the two streams times is used for the next sleep interval.
TEST_F(TimingSuite, WaitTwoStreamsDifferentWakeupTimes) {
  cras_audio_format s1_format, s2_format;
  fill_audio_format(&s1_format, 44100);
  fill_audio_format(&s2_format, 48000);

  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);

  // stream1's next callback is in 3ms.
  StreamPtr stream1 =
      create_stream(1, 1, CRAS_STREAM_INPUT, 441, &s1_format);
  stream1->rstream->next_cb_ts = start;
  const timespec three_millis = { 0, 3 * 1000 * 1000 };
  add_timespecs(&stream1->rstream->next_cb_ts, &three_millis);
  AddFakeDataToStream(stream1.get(), 441);
  // stream2 is also ready next cb in 5ms..
  StreamPtr stream2  =
      create_stream(1, 1, CRAS_STREAM_INPUT, 480, &s2_format);
  stream2->rstream->next_cb_ts = start;
  const timespec five_millis = { 0, 5 * 1000 * 1000 };
  add_timespecs(&stream2->rstream->next_cb_ts, &five_millis);
  AddFakeDataToStream(stream1.get(), 480);

  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream1));
  streams.emplace_back(std::move(stream2));
  timespec dev_time = SingleInputDevNextWake(441, 441, &start,
                                             &s1_format, streams);

  // Should wait for approximately 3 milliseconds for stream 1 first.
  struct timespec delta2;
  subtract_timespecs(&dev_time, &start, &delta2);
  EXPECT_LT(2900 * 1000, delta2.tv_nsec);
  EXPECT_GT(3100 * 1000, delta2.tv_nsec);
}

// One hotword stream attaches to hotword device. Input data has copied from
// device to stream but total number is less than cb_threshold. Hotword stream
// should be scheduled wake base on the samples needed to fill full shm.
TEST_F(TimingSuite, HotwordStreamUseDevTiming) {
  cras_audio_format fmt;
  fill_audio_format(&fmt, 48000);

  struct timespec start, delay;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, 240, &fmt);
  stream->rstream->flags = HOTWORD_STREAM;
  stream->rstream->next_cb_ts = start;
  delay.tv_sec = 0;
  delay.tv_nsec = 3 * 1000 * 1000;
  add_timespecs(&stream->rstream->next_cb_ts, &delay);

  // Add fake data to stream and device so its slightly less than cb_threshold.
  // Expect to wait for samples to fill the full buffer (480 - 192) frames
  // instead of using the next_cb_ts.
  AddFakeDataToStream(stream.get(), 192);
  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  timespec dev_time = SingleInputDevNextWake(4096, 0, &start,
                                             &fmt, streams);
  struct timespec delta;
  subtract_timespecs(&dev_time, &start, &delta);
  // 288 frames worth of time = 6 ms.
  EXPECT_EQ(6 * 1000 * 1000, delta.tv_nsec);
}

// One hotword stream attaches to hotword device. Input data burst to a number
// larger than cb_threshold. Also, stream is pending client reply.
// In this case stream fd is used to poll for next wake.
// And the dev wake time is unchanged from the default 20 seconds limit.
TEST_F(TimingSuite, HotwordStreamBulkDataIsPending) {
  int poll_fd = 0;
  cras_audio_format fmt;
  fill_audio_format(&fmt, 48000);

  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, 240, &fmt);
  stream->rstream->flags = HOTWORD_STREAM;
  stream->rstream->next_cb_ts = start;

  AddFakeDataToStream(stream.get(), 480);
  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  // Stream is pending the reply from client.
  rstream_stub_pending_reply(streams[0]->rstream.get(), 1);

  // There is more than 1 cb_threshold of data in device.
  timespec dev_time = SingleInputDevNextWake(
      4096, 7000, &start, &fmt, streams, CRAS_NODE_TYPE_HOTWORD);

  // Need to wait for stream fd in the next ppoll.
  poll_fd = dev_stream_poll_stream_fd(streams[0]->dstream.get());
  EXPECT_EQ(FAKE_POLL_FD, poll_fd);

  struct timespec delta;
  subtract_timespecs(&dev_time, &start, &delta);
  // Wake up time should be default 20 seconds because audio thread
  // depends on reply from client to wake it up.
  EXPECT_LT(19, delta.tv_sec);
  EXPECT_GT(21, delta.tv_sec);
}

// One hotword stream attaches to hotword device. Input data burst to a number
// larger than cb_threshold. However, stream is not pending client reply.
// This happens if there was no data during capture_to_stream.
// In this case stream fd is NOT used to poll for next wake.
// And the dev wake time is changed to a 0 instead of default 20 seconds.
TEST_F(TimingSuite, HotwordStreamBulkDataIsNotPending) {
  int poll_fd = 0;
  cras_audio_format fmt;
  fill_audio_format(&fmt, 48000);

  struct timespec start;
  clock_gettime(CLOCK_MONOTONIC_RAW, &start);

  StreamPtr stream =
      create_stream(1, 1, CRAS_STREAM_INPUT, 240, &fmt);
  stream->rstream->flags = HOTWORD_STREAM;
  stream->rstream->next_cb_ts = start;

  AddFakeDataToStream(stream.get(), 480);
  std::vector<StreamPtr> streams;
  streams.emplace_back(std::move(stream));
  // Stream is not pending the reply from client.
  rstream_stub_pending_reply(streams[0]->rstream.get(), 0);

  // There is more than 1 cb_threshold of data in device.
  timespec dev_time = SingleInputDevNextWake(4096, 7000, &start,
                                             &fmt, streams);

  // Does not need to wait for stream fd in the next ppoll.
  poll_fd = dev_stream_poll_stream_fd(streams[0]->dstream.get());
  EXPECT_EQ(-1, poll_fd);

  struct timespec delta;
  subtract_timespecs(&dev_time, &start, &delta);
  // Wake up time should be very small because there is enough
  // data to be send to client.
  EXPECT_LT(delta.tv_sec, 0.1);
}

/* Stubs */
extern "C" {

int cras_server_metrics_highest_hw_level(unsigned hw_level,
		enum CRAS_STREAM_DIRECTION direction)
{
  return 0;
}

int cras_server_metrics_longest_fetch_delay(unsigned delay_msec)
{
  return 0;
}

int cras_server_metrics_num_underruns(unsigned num_underruns)
{
  return 0;
}

int input_data_get_for_stream(
		struct input_data *data,
		struct cras_rstream *stream,
		struct buffer_share *offsets,
		struct cras_audio_area **area,
		unsigned int *offset)
{
  return 0;
}

int input_data_put_for_stream(struct input_data *data,
			   struct cras_rstream *stream,
			   struct buffer_share *offsets,
			   unsigned int frames)
{
  return 0;
}
struct cras_audio_format *cras_rstream_post_processing_format(
    const struct cras_rstream *stream, void *dev_ptr)
{
  return NULL;
}
}  // extern "C"

}  //  namespace

int main(int argc, char** argv) {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}