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"
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
;
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",
32 AUDIO_RENDERER_AUDIO_GLITCHES_MAX
+ 1);
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),
50 // TODO(dalecurtis): Investigate if we can reduce this on all platforms.
51 maximum_wait_time_(base::TimeDelta::FromMilliseconds(20)),
54 DCHECK_EQ(packet_size_
, AudioBus::CalculateMemorySize(params
));
55 output_bus_
= AudioBus::WrapMemory(params
, shared_memory
->memory());
59 AudioSyncReader::~AudioSyncReader() {
60 if (!renderer_callback_count_
)
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.
88 socket_
->Send(&bytes
, sizeof(bytes
));
92 void AudioSyncReader::Read(AudioBus
* dest
) {
93 ++renderer_callback_count_
;
94 if (!WaitUntilDataIsReady()) {
95 ++renderer_missed_callback_count_
;
103 output_bus_
->CopyTo(dest
);
106 void AudioSyncReader::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
)) {
151 if (renderer_buffer_index
== buffer_index_
)
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",
166 base::TimeDelta::FromMilliseconds(1),
167 base::TimeDelta::FromMilliseconds(1000),
175 } // namespace content