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.