chromium/chromecast/media/audio/fake_external_audio_pipeline.cc

// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <cstdint>
#include <memory>
#include <vector>

#include "base/check.h"
#include "base/no_destructor.h"
#include "base/ranges/algorithm.h"
#include "chromecast/media/audio/fake_external_audio_pipeline_support.h"
#include "chromecast/public/cast_media_shlib.h"
#include "chromecast/public/media/external_audio_pipeline_shlib.h"
#include "chromecast/public/media/mixer_output_stream.h"

namespace chromecast {
namespace media {
namespace {

// Library implementation for media volume/mute testing part. It stores
// observers for sending volume/mute change requests and stores volume/muted.
class TestMediaVolumeMute {
 public:
  TestMediaVolumeMute() = default;

  TestMediaVolumeMute(const TestMediaVolumeMute&) = delete;
  TestMediaVolumeMute& operator=(const TestMediaVolumeMute&) = delete;

  // Called by library.
  void AddExternalMediaVolumeChangeRequestObserver(
      ExternalAudioPipelineShlib::ExternalMediaVolumeChangeRequestObserver*
          observer) {
    volume_change_request_observer_ = observer;
  }
  void RemoveExternalMediaVolumeChangeRequestObserver(
      ExternalAudioPipelineShlib::ExternalMediaVolumeChangeRequestObserver*
          observer) {
    volume_change_request_observer_ = nullptr;
  }

  void SetVolume(float volume) { volume_ = volume; }
  void SetMuted(bool muted) { muted_ = muted; }

 protected:
  // Used by derived class for FakeExternalAudioPipelineSupport.
  ExternalAudioPipelineShlib::ExternalMediaVolumeChangeRequestObserver*
      volume_change_request_observer_ = nullptr;
  float volume_ = 0;
  bool muted_ = false;
};

// Library implementation for loopback data testing part. It stores
// CastMediaShlib observers and does loopback audio data to it.
class TestLoopBack {
 public:
  TestLoopBack() = default;

  TestLoopBack(const TestLoopBack&) = delete;
  TestLoopBack& operator=(const TestLoopBack&) = delete;

  // Called from FakeMixerOutputStream.
  void OnData(const float* data,
              int data_size,
              MixerOutputStream* stream,
              int channels) {
    auto delay = stream->GetRenderingDelay();
    int64_t delay_microseconds =
        delay.timestamp_microseconds + delay.delay_microseconds;
    for (auto* observer : observers_) {
      observer->OnLoopbackAudio(
          delay_microseconds, kSampleFormatF32, stream->GetSampleRate(),
          channels, reinterpret_cast<uint8_t*>(const_cast<float*>(data)),
          data_size * sizeof(float));
    }
  }
  // Called from library.
  void AddExternalLoopbackAudioObserver(
      ExternalAudioPipelineShlib::LoopbackAudioObserver* observer) {
    observers_.push_back(observer);
  }

  void RemoveExternalLoopbackAudioObserver(
      ExternalAudioPipelineShlib::LoopbackAudioObserver* observer) {
    auto it = base::ranges::find(observers_, observer);
    if (it != observers_.end()) {
      observers_.erase(it);
    }
    observer->OnRemoved();
  }

 protected:
  // Used by derived class for FakeExternalAudioPipelineSupport.
  std::vector<ExternalAudioPipelineShlib::LoopbackAudioObserver*> observers_;
};

class TestMediaMetadata {
 public:
  TestMediaMetadata() = default;

  TestMediaMetadata(const TestMediaMetadata&) = delete;
  TestMediaMetadata& operator=(const TestMediaMetadata&) = delete;

  // Called from library.
  void AddExternalMediaMetadataChangeObserver(
      ExternalAudioPipelineShlib::ExternalMediaMetadataChangeObserver*
          observer) {
    media_metadata_change_observer_ = observer;
  }
  void RemoveExternalMediaMetadataChangeObserver(
      ExternalAudioPipelineShlib::ExternalMediaMetadataChangeObserver*) {
    media_metadata_change_observer_ = nullptr;
  }

 protected:
  ExternalAudioPipelineShlib::ExternalMediaMetadataChangeObserver*
      media_metadata_change_observer_ = nullptr;
};

// Final class includes library implementation for testing media volume/mute
// + loopback and FakeExternalAudioPipelineSupport implementation.
class TestMedia : public TestMediaVolumeMute,
                  public TestLoopBack,
                  public TestMediaMetadata,
                  public testing::FakeExternalAudioPipelineSupport {
 public:
  TestMedia() = default;

  TestMedia(const TestMedia&) = delete;
  TestMedia& operator=(const TestMedia&) = delete;

  bool supported() const { return supported_; }

  // FakeExternalAudioPipelineSupport implementation:
  void SetSupported() override { supported_ = true; }

  void Reset() override {
    supported_ = false;
    volume_change_request_observer_ = nullptr;
    volume_ = 0;
    muted_ = false;
    observers_.clear();
  }

  float GetVolume() const override { return volume_; }
  bool IsMuted() const override { return muted_; }

  void OnVolumeChangeRequest(float level) override {
    CHECK(volume_change_request_observer_);
    volume_change_request_observer_->OnVolumeChangeRequest(level);
  }
  void OnMuteChangeRequest(bool muted) override {
    CHECK(volume_change_request_observer_);
    volume_change_request_observer_->OnMuteChangeRequest(muted);
  }

  void UpdateExternalMediaMetadata(
      const ExternalAudioPipelineShlib::ExternalMediaMetadata& metadata)
      override {
    if (media_metadata_change_observer_) {
      media_metadata_change_observer_->OnExternalMediaMetadataChanged(metadata);
    }
  }

 private:
  bool supported_ = false;
};

TestMedia* GetTestMedia() {
  static base::NoDestructor<TestMedia> g_test_media;
  return g_test_media.get();
}

// MixerOutputStream implementation, it will be created by
// ExternalAudioPipelineShlib::CreateMixerOutputStream.
class FakeMixerOutputStream : public MixerOutputStream {
 public:
  FakeMixerOutputStream() : test_loop_back_(GetTestMedia()) {}

  FakeMixerOutputStream(const FakeMixerOutputStream&) = delete;
  FakeMixerOutputStream& operator=(const FakeMixerOutputStream&) = delete;

  // MixerOutputStream implementation:
  bool Start(int requested_sample_rate, int channels) override {
    sample_rate_ = requested_sample_rate;
    channels_ = channels;
    return true;
  }

  void Stop() override {}

  int GetNumChannels() override { return channels_; }
  int GetSampleRate() override { return sample_rate_; }

  MediaPipelineBackend::AudioDecoder::RenderingDelay GetRenderingDelay()
      override {
    return MediaPipelineBackend::AudioDecoder::RenderingDelay();
  }

  int OptimalWriteFramesCount() override { return 256; }

  bool Write(const float* data,
             int data_size,
             bool* out_playback_interrupted) override {
    // To check OnLoopbackInterrupted.
    *out_playback_interrupted = true;
    // Loopback data.
    test_loop_back_->OnData(data, data_size, this, channels_);
    return true;
  }

 private:
  int sample_rate_ = 0;
  int channels_ = 0;
  TestLoopBack* const test_loop_back_;
};

}  // namespace

namespace testing {
// Get the interface for interaction with the library from unittests.
FakeExternalAudioPipelineSupport* GetFakeExternalAudioPipelineSupport() {
  return GetTestMedia();
}

}  // namespace testing

// Library implementation.
bool ExternalAudioPipelineShlib::IsSupported() {
  return GetTestMedia()->supported();
}

void ExternalAudioPipelineShlib::AddExternalMediaVolumeChangeRequestObserver(
    ExternalMediaVolumeChangeRequestObserver* observer) {
  GetTestMedia()->AddExternalMediaVolumeChangeRequestObserver(observer);
}

void ExternalAudioPipelineShlib::RemoveExternalMediaVolumeChangeRequestObserver(
    ExternalMediaVolumeChangeRequestObserver* observer) {
  GetTestMedia()->RemoveExternalMediaVolumeChangeRequestObserver(observer);
}

void ExternalAudioPipelineShlib::SetExternalMediaVolume(float level) {
  GetTestMedia()->SetVolume(level);
}

void ExternalAudioPipelineShlib::SetExternalMediaMuted(bool muted) {
  GetTestMedia()->SetMuted(muted);
}

void ExternalAudioPipelineShlib::AddExternalLoopbackAudioObserver(
    LoopbackAudioObserver* observer) {
  GetTestMedia()->AddExternalLoopbackAudioObserver(observer);
}

void ExternalAudioPipelineShlib::RemoveExternalLoopbackAudioObserver(
    LoopbackAudioObserver* observer) {
  GetTestMedia()->RemoveExternalLoopbackAudioObserver(observer);
}

void ExternalAudioPipelineShlib::AddExternalMediaMetadataChangeObserver(
    ExternalMediaMetadataChangeObserver* observer) {
  GetTestMedia()->AddExternalMediaMetadataChangeObserver(observer);
}

void ExternalAudioPipelineShlib::RemoveExternalMediaMetadataChangeObserver(
    ExternalMediaMetadataChangeObserver* observer) {
  GetTestMedia()->RemoveExternalMediaMetadataChangeObserver(observer);
}

std::unique_ptr<MixerOutputStream>
ExternalAudioPipelineShlib::CreateMixerOutputStream() {
  return std::make_unique<FakeMixerOutputStream>();
}

}  // namespace media
}  // namespace chromecast