1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "content/browser/renderer_host/media/audio_sync_reader.h"
6
7 #include <algorithm>
8
9 #include "base/command_line.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/browser/renderer_host/media/media_stream_manager.h"
14 #include "content/public/common/content_switches.h"
15 #include "media/audio/audio_buffers_state.h"
16 #include "media/audio/audio_parameters.h"
17
18 using media::AudioBus;
19
20 namespace {
21
22 // Used to log if any audio glitches have been detected during an audio session.
23 // Elements in this enum should not be added, deleted or rearranged.
24 enum AudioGlitchResult {
25 AUDIO_RENDERER_NO_AUDIO_GLITCHES = 0,
26 AUDIO_RENDERER_AUDIO_GLITCHES = 1,
27 AUDIO_RENDERER_AUDIO_GLITCHES_MAX = AUDIO_RENDERER_AUDIO_GLITCHES
28 };
29
LogAudioGlitchResult(AudioGlitchResult result)30 void LogAudioGlitchResult(AudioGlitchResult result) {
31 UMA_HISTOGRAM_ENUMERATION("Media.AudioRendererAudioGlitches",
32 result,
33 AUDIO_RENDERER_AUDIO_GLITCHES_MAX + 1);
34 }
35
36 } // namespace
37
38 namespace content {
39
AudioSyncReader(base::SharedMemory * shared_memory,const media::AudioParameters & params)40 AudioSyncReader::AudioSyncReader(base::SharedMemory* shared_memory,
41 const media::AudioParameters& params)
42 : shared_memory_(shared_memory),
43 mute_audio_(CommandLine::ForCurrentProcess()->HasSwitch(
44 switches::kMuteAudio)),
45 packet_size_(shared_memory_->requested_size()),
46 renderer_callback_count_(0),
47 renderer_missed_callback_count_(0),
48 #if defined(OS_MACOSX)
49 maximum_wait_time_(params.GetBufferDuration() / 2),
50 #else
51 // TODO(dalecurtis): Investigate if we can reduce this on all platforms.
52 maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)),
53 #endif
54 buffer_index_(0) {
55 DCHECK_EQ(packet_size_, AudioBus::CalculateMemorySize(params));
56 output_bus_ = AudioBus::WrapMemory(params, shared_memory->memory());
57 output_bus_->Zero();
58 }
59
~AudioSyncReader()60 AudioSyncReader::~AudioSyncReader() {
61 if (!renderer_callback_count_)
62 return;
63
64 // Recording the percentage of deadline misses gives us a rough overview of
65 // how many users might be running into audio glitches.
66 int percentage_missed =
67 100.0 * renderer_missed_callback_count_ / renderer_callback_count_;
68 UMA_HISTOGRAM_PERCENTAGE(
69 "Media.AudioRendererMissedDeadline", percentage_missed);
70
71 // Add more detailed information regarding detected audio glitches where
72 // a non-zero value of |renderer_missed_callback_count_| is added to the
73 // AUDIO_RENDERER_AUDIO_GLITCHES bin.
74 renderer_missed_callback_count_ > 0 ?
75 LogAudioGlitchResult(AUDIO_RENDERER_AUDIO_GLITCHES) :
76 LogAudioGlitchResult(AUDIO_RENDERER_NO_AUDIO_GLITCHES);
77 std::string log_string =
78 base::StringPrintf("ASR: number of detected audio glitches=%d",
79 static_cast<int>(renderer_missed_callback_count_));
80 MediaStreamManager::SendMessageToNativeLog(log_string);
81 DVLOG(1) << log_string;
82 }
83
84 // media::AudioOutputController::SyncReader implementations.
UpdatePendingBytes(uint32 bytes)85 void AudioSyncReader::UpdatePendingBytes(uint32 bytes) {
86 // Zero out the entire output buffer to avoid stuttering/repeating-buffers
87 // in the anomalous case if the renderer is unable to keep up with real-time.
88 output_bus_->Zero();
89 socket_->Send(&bytes, sizeof(bytes));
90 ++buffer_index_;
91 }
92
Read(AudioBus * dest)93 void AudioSyncReader::Read(AudioBus* dest) {
94 ++renderer_callback_count_;
95 if (!WaitUntilDataIsReady()) {
96 ++renderer_missed_callback_count_;
97 dest->Zero();
98 return;
99 }
100
101 if (mute_audio_)
102 dest->Zero();
103 else
104 output_bus_->CopyTo(dest);
105 }
106
Close()107 void AudioSyncReader::Close() {
108 socket_->Close();
109 }
110
Init()111 bool AudioSyncReader::Init() {
112 socket_.reset(new base::CancelableSyncSocket());
113 foreign_socket_.reset(new base::CancelableSyncSocket());
114 return base::CancelableSyncSocket::CreatePair(socket_.get(),
115 foreign_socket_.get());
116 }
117
PrepareForeignSocket(base::ProcessHandle process_handle,base::SyncSocket::TransitDescriptor * descriptor)118 bool AudioSyncReader::PrepareForeignSocket(
119 base::ProcessHandle process_handle,
120 base::SyncSocket::TransitDescriptor* descriptor) {
121 return foreign_socket_->PrepareTransitDescriptor(process_handle, descriptor);
122 }
123
WaitUntilDataIsReady()124 bool AudioSyncReader::WaitUntilDataIsReady() {
125 base::TimeDelta timeout = maximum_wait_time_;
126 const base::TimeTicks start_time = base::TimeTicks::Now();
127 const base::TimeTicks finish_time = start_time + timeout;
128
129 // Check if data is ready and if not, wait a reasonable amount of time for it.
130 //
131 // Data readiness is achieved via parallel counters, one on the renderer side
132 // and one here. Every time a buffer is requested via UpdatePendingBytes(),
133 // |buffer_index_| is incremented. Subsequently every time the renderer has a
134 // buffer ready it increments its counter and sends the counter value over the
135 // SyncSocket. Data is ready when |buffer_index_| matches the counter value
136 // received from the renderer.
137 //
138 // The counter values may temporarily become out of sync if the renderer is
139 // unable to deliver audio fast enough. It's assumed that the renderer will
140 // catch up at some point, which means discarding counter values read from the
141 // SyncSocket which don't match our current buffer index.
142 size_t bytes_received = 0;
143 uint32 renderer_buffer_index = 0;
144 while (timeout.InMicroseconds() > 0) {
145 bytes_received = socket_->ReceiveWithTimeout(
146 &renderer_buffer_index, sizeof(renderer_buffer_index), timeout);
147 if (bytes_received != sizeof(renderer_buffer_index)) {
148 bytes_received = 0;
149 break;
150 }
151
152 if (renderer_buffer_index == buffer_index_)
153 break;
154
155 // Reduce the timeout value as receives succeed, but aren't the right index.
156 timeout = finish_time - base::TimeTicks::Now();
157 }
158
159 // Receive timed out or another error occurred. Receive can timeout if the
160 // renderer is unable to deliver audio data within the allotted time.
161 if (!bytes_received || renderer_buffer_index != buffer_index_) {
162 DVLOG(2) << "AudioSyncReader::WaitUntilDataIsReady() timed out.";
163
164 base::TimeDelta time_since_start = base::TimeTicks::Now() - start_time;
165 UMA_HISTOGRAM_CUSTOM_TIMES("Media.AudioOutputControllerDataNotReady",
166 time_since_start,
167 base::TimeDelta::FromMilliseconds(1),
168 base::TimeDelta::FromMilliseconds(1000),
169 50);
170 return false;
171 }
172
173 return true;
174 }
175
176 } // namespace content
177