1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135

media / audio / audio_device_thread.cc [blame]

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

#include "media/audio/audio_device_thread.h"

#include <limits>
#include <ostream>

#include "base/check_op.h"
#include "base/containers/span.h"
#include "base/system/sys_info.h"
#include "build/build_config.h"
#include "media/base/media_switches.h"

namespace media {

// AudioDeviceThread::Callback implementation

AudioDeviceThread::Callback::Callback(const AudioParameters& audio_parameters,
                                      uint32_t segment_length,
                                      uint32_t total_segments)
    : audio_parameters_(audio_parameters),
      memory_length_(
          base::CheckMul(segment_length, total_segments).ValueOrDie()),
      total_segments_(total_segments),
      segment_length_(segment_length) {
  CHECK_GT(total_segments_, 0u);
  thread_checker_.DetachFromThread();
}

AudioDeviceThread::Callback::~Callback() = default;

void AudioDeviceThread::Callback::InitializeOnAudioThread() {
  // Normally this function is called before the thread checker is used
  // elsewhere, but it's not guaranteed. DCHECK to ensure it was not used on
  // another thread before we get here.
  DCHECK(thread_checker_.CalledOnValidThread())
      << "Thread checker was attached on the wrong thread";
  MapSharedMemory();
}

bool AudioDeviceThread::Callback::WillConfirmReadsViaShmem() const {
  return false;
}

// AudioDeviceThread implementation

AudioDeviceThread::AudioDeviceThread(Callback* callback,
                                     base::SyncSocket::ScopedHandle socket,
                                     const char* thread_name,
                                     base::ThreadType thread_type)
    : callback_(callback),
      thread_name_(thread_name),
      socket_(std::move(socket)),
      send_socket_messages_(!callback_->WillConfirmReadsViaShmem()) {
#if defined(ARCH_CPU_X86)
  // Audio threads don't need a huge stack, they don't have a message loop and
  // they are used exclusively for polling the next frame of audio. See
  // https://crbug.com/1141563 for discussion.
  constexpr size_t kStackSize = 256 * 1024;
#else
  constexpr size_t kStackSize = 0;  // Default.
#endif

  CHECK(base::PlatformThread::CreateWithType(kStackSize, this, &thread_handle_,
                                             thread_type));

  DCHECK(!thread_handle_.is_null());
}

AudioDeviceThread::~AudioDeviceThread() {
  in_shutdown_.Set();
  socket_.Shutdown();
  if (thread_handle_.is_null())
    return;
  base::PlatformThread::Join(thread_handle_);
}

#if BUILDFLAG(IS_APPLE)
base::TimeDelta AudioDeviceThread::GetRealtimePeriod() {
  return callback_->buffer_duration();
}
#endif

void AudioDeviceThread::ThreadMain() {
  base::PlatformThread::SetName(thread_name_);
  callback_->InitializeOnAudioThread();

  uint32_t buffer_index = 0;
  while (true) {
    uint32_t pending_data = 0;
    size_t bytes_read = socket_.Receive(base::byte_span_from_ref(pending_data));
    if (bytes_read != sizeof(pending_data))
      break;

    // std::numeric_limits<uint32_t>::max() is a special signal which is
    // returned after the browser stops the output device in response to a
    // renderer side request.
    //
    // Avoid running Process() for the paused signal, we still need to update
    // the buffer index for synchronized buffers though.
    //
    // See comments in AudioOutputController::DoPause() for details on why.
    if (pending_data != std::numeric_limits<uint32_t>::max())
      callback_->Process(pending_data);

    // If `send_socket_messages_` is false, the socket messages are replaced by
    // a flag in shared memory which is handled in `callback_->Process()`.
    if (send_socket_messages_) {
      // The usage of the socket messages differs between input and output
      // cases.
      //
      // Input: We send a socket message to let the other end know that we have
      // read data, so that it can verify that it doesn't overwrite any data
      // before read.
      //
      // Output: Let the other end know which buffer we just filled. The
      // `buffer_index` is used to ensure the other end is getting the buffer it
      // expects. For more details on how this works see
      // `SyncReader::WaitUntilDataIsReady()`.
      ++buffer_index;
      size_t bytes_sent = socket_.Send(base::byte_span_from_ref(buffer_index));
      if (bytes_sent != sizeof(buffer_index)) {
        break;
      }
    }
  }

  if (!in_shutdown_.IsSet()) {
    callback_->OnSocketError();
  }
}

}  // namespace media.