chromium/chromecast/media/audio/mixer_service/redirected_audio_connection.cc

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

#include "chromecast/media/audio/mixer_service/redirected_audio_connection.h"

#include <algorithm>
#include <limits>

#include "base/check_op.h"
#include "chromecast/media/audio/mixer_service/mixer_service_transport.pb.h"
#include "chromecast/media/audio/net/common.pb.h"
#include "chromecast/media/audio/net/conversions.h"
#include "chromecast/net/io_buffer_pool.h"

namespace chromecast {
namespace media {
namespace mixer_service {

namespace {

void FillPatterns(
    const RedirectedAudioConnection::StreamMatchPatterns& patterns,
    Generic* message) {
  RedirectedStreamPatterns* patterns_proto =
      message->mutable_redirected_stream_patterns();
  for (const auto& p : patterns) {
    auto* pattern = patterns_proto->add_patterns();
    pattern->set_content_type(audio_service::ConvertContentType(p.first));
    pattern->set_device_id_pattern(p.second);
  }
}

enum MessageTypes : int {
  kRedirectionRequest = 1,
  kStreamMatchPatterns,
};

}  // namespace

RedirectedAudioConnection::RedirectedAudioConnection(const Config& config,
                                                     Delegate* delegate)
    : config_(config), delegate_(delegate) {
  DCHECK(delegate_);
}

RedirectedAudioConnection::~RedirectedAudioConnection() = default;

void RedirectedAudioConnection::SetStreamMatchPatterns(
    StreamMatchPatterns patterns) {
  stream_match_patterns_ = std::move(patterns);
  if (socket_) {
    Generic message;
    FillPatterns(stream_match_patterns_, &message);
    socket_->SendProto(kStreamMatchPatterns, message);
  }
}

void RedirectedAudioConnection::Connect() {
  MixerConnection::Connect();
}

void RedirectedAudioConnection::ConnectForTest(
    std::unique_ptr<MixerSocket> connected_socket_for_test) {
  DCHECK(connected_socket_for_test);
  OnConnected(std::move(connected_socket_for_test));
}

void RedirectedAudioConnection::OnConnected(
    std::unique_ptr<MixerSocket> socket) {
  sample_rate_ = 0;

  socket_ = std::move(socket);
  socket_->SetDelegate(this);

  Generic message;
  RedirectionRequest* request = message.mutable_redirection_request();
  request->set_order(config_.order);
  request->set_num_channels(config_.num_output_channels);
  if (config_.output_channel_layout != media::ChannelLayout::UNSUPPORTED &&
      config_.output_channel_layout != media::ChannelLayout::BITSTREAM) {
    request->set_channel_layout(
        audio_service::ConvertChannelLayout(config_.output_channel_layout));
  }
  request->set_apply_volume(config_.apply_volume);
  request->set_extra_delay_microseconds(config_.extra_delay_microseconds);

  if (!stream_match_patterns_.empty()) {
    FillPatterns(stream_match_patterns_, &message);
  }
  socket_->SendProto(kRedirectionRequest, message);
}

void RedirectedAudioConnection::OnConnectionError() {
  socket_.reset();
  MixerConnection::Connect();
}

bool RedirectedAudioConnection::HandleMetadata(const Generic& message) {
  if (message.has_stream_config()) {
    DCHECK_EQ(message.stream_config().sample_format(),
              audio_service::SAMPLE_FORMAT_FLOAT_P);
    sample_rate_ = message.stream_config().sample_rate();
    DCHECK_EQ(message.stream_config().num_channels(),
              config_.num_output_channels);

    delegate_->SetSampleRate(sample_rate_);
  }
  return true;
}

bool RedirectedAudioConnection::HandleAudioData(char* data,
                                                size_t size,
                                                int64_t timestamp) {
  if (sample_rate_ != 0) {
    int frames = size / (sizeof(float) * config_.num_output_channels);
    delegate_->OnRedirectedAudio(timestamp, reinterpret_cast<float*>(data),
                                 frames);
  }
  return true;
}

}  // namespace mixer_service
}  // namespace media
}  // namespace chromecast