chromium/chromecast/media/cma/backend/mixer/audio_output_redirector.h

// 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.

#ifndef CHROMECAST_MEDIA_CMA_BACKEND_MIXER_AUDIO_OUTPUT_REDIRECTOR_H_
#define CHROMECAST_MEDIA_CMA_BACKEND_MIXER_AUDIO_OUTPUT_REDIRECTOR_H_

#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/weak_ptr.h"
#include "chromecast/media/audio/mixer_service/redirected_audio_connection.h"
#include "chromecast/net/io_buffer_pool.h"
#include "chromecast/public/media/media_pipeline_backend.h"
#include "chromecast/public/volume_control.h"
#include "media/base/channel_layout.h"

namespace base {
class SequencedTaskRunner;
}  // namespace base

namespace media {
class AudioBus;
}  // namespace media

namespace chromecast {
namespace media {
class MixerInput;
class StreamMixer;

namespace mixer_service {
class Generic;
class MixerSocket;
}  // namespace mixer_service

// The AudioOutputRedirector class determines which MixerInputs match the config
// conditions, and adds an AudioOutputRedirectorInput to each one.
// When the mixer is writing output, it tells each AudioOutputRedirector to
// create a new buffer (by calling PrepareNextBuffer()). As audio is pulled from
// each MixerInput, the resulting audio is passed through any added
// AudioOutputRedirectorInputs and is mixed into the redirected buffer by the
// AudioOutputRedirector (with appropriate fading to avoid pops/clicks, and
// volume applied if desired). Once all MixerInputs have been processed, the
// mixer will call FinishBuffer() on each AudioOutputRedirector; the
// redirected buffer is then sent to the RedirectedAudioOutput associated
// with each redirector.
// Created on the IO thread, but otherwise runs on the mixer thread.
class AudioOutputRedirector {
 public:
  using RenderingDelay = MediaPipelineBackend::AudioDecoder::RenderingDelay;

  AudioOutputRedirector(StreamMixer* mixer,
                        std::unique_ptr<mixer_service::MixerSocket> socket,
                        const mixer_service::Generic& message);

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

  ~AudioOutputRedirector();

  int order() const { return config_.order; }
  int num_output_channels() const { return config_.num_output_channels; }
  ::media::ChannelLayout output_channel_layout() const {
    return output_channel_layout_;
  }

  int64_t extra_delay_microseconds() const {
    return config_.extra_delay_microseconds;
  }

  // Adds/removes mixer inputs. The mixer adds/removes all mixer inputs from
  // the AudioOutputRedirector; this class does the work to determine which
  // inputs match the desired conditions.
  void AddInput(MixerInput* mixer_input);
  void RemoveInput(MixerInput* mixer_input);

  // Sets the sample rate for subsequent audio from inputs.
  void SetSampleRate(int output_samples_per_second);

  // Called by the mixer when it is preparing to write another buffer of
  // |num_frames| frames.
  void PrepareNextBuffer(int num_frames);

  // Mixes audio into the redirected output buffer. Called by the
  // AudioOutputRedirectorInput implementation to mix in audio from each
  // matching MixerInput.
  void MixInput(MixerInput* mixer_input,
                ::media::AudioBus* data,
                int num_frames,
                RenderingDelay rendering_delay);

  // Called by the mixer once all MixerInputs have been processed; passes the
  // redirected audio buffer to the output plugin.
  void FinishBuffer();

 private:
  class InputImpl;
  class RedirectionConnection;

  using Config = mixer_service::RedirectedAudioConnection::Config;

  static Config ParseConfig(const mixer_service::Generic& message);

  // Updates the set of patterns used to determine which inputs should be
  // redirected by this AudioOutputRedirector. Any inputs which no longer match
  // will stop being redirected.
  void UpdatePatterns(
      std::vector<std::pair<AudioContentType, std::string>> patterns);
  void OnConnectionError();

  bool ApplyToInput(MixerInput* mixer_input);

  StreamMixer* const mixer_;
  const Config config_;
  const ::media::ChannelLayout output_channel_layout_;
  std::unique_ptr<RedirectionConnection> output_;
  scoped_refptr<base::SequencedTaskRunner> io_task_runner_;

  int sample_rate_ = 0;

  std::vector<std::pair<AudioContentType, std::string>> patterns_;

  int next_num_frames_ = 0;
  int64_t next_output_timestamp_ = INT64_MIN;
  int input_count_ = 0;

  scoped_refptr<IOBufferPool> buffer_pool_;
  scoped_refptr<::net::IOBuffer> current_mix_buffer_;
  float* current_mix_data_ = nullptr;

  base::flat_map<MixerInput*, std::unique_ptr<InputImpl>> inputs_;
  base::flat_set<MixerInput*> non_redirected_inputs_;

  base::WeakPtrFactory<AudioOutputRedirector> weak_factory_;
};

}  // namespace media
}  // namespace chromecast

#endif  // CHROMECAST_MEDIA_CMA_BACKEND_MIXER_AUDIO_OUTPUT_REDIRECTOR_H_