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